Bug 767633 - Part 2 - nsObjectLoadingContent should handle all initial streams for plugins. r=josh
authorJohn Schoenick <jschoenick@mozilla.com>
Thu, 06 Dec 2012 15:10:54 -0800
changeset 121552 1b68ff48e22c35a471bfed05e8222929399a9b53
parent 121551 6aed5f5caecb8eb5fe6fb610d70bf08452e52587
child 121553 11341616d7cb84252fe339b8e1bec3b2ae3dd3c3
push idunknown
push userunknown
push dateunknown
reviewersjosh
bugs767633
milestone20.0a1
Bug 767633 - Part 2 - nsObjectLoadingContent should handle all initial streams for plugins. r=josh
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsObjectLoadingContent.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginHost.h
dom/plugins/base/nsPluginStreamListenerPeer.cpp
dom/plugins/base/nsPluginStreamListenerPeer.h
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -527,16 +527,45 @@ IsPluginEnabledForType(const nsCString& 
 
   return NS_OK;
 }
 
 ///
 /// Member Functions
 ///
 
+// Tedious syntax to create a plugin stream listener with checks and put it in
+// mFinalListener
+bool
+nsObjectLoadingContent::MakePluginListener()
+{
+  if (!mInstanceOwner) {
+    NS_NOTREACHED("expecting a spawned plugin");
+    return false;
+  }
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+  if (!pluginHost) {
+    NS_NOTREACHED("No pluginHost");
+    return false;
+  }
+  NS_ASSERTION(!mFinalListener, "overwriting a final listener");
+  nsresult rv;
+  nsRefPtr<nsNPAPIPluginInstance> inst;
+  nsCOMPtr<nsIStreamListener> finalListener;
+  rv = mInstanceOwner->GetInstance(getter_AddRefs(inst));
+  NS_ENSURE_SUCCESS(rv, false);
+  rv = pluginHost->NewEmbeddedPluginStreamListener(mURI, inst,
+                                                   getter_AddRefs(finalListener));
+  NS_ENSURE_SUCCESS(rv, false);
+  mFinalListener = finalListener;
+  return true;
+}
+
+
 bool
 nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
   nsCOMPtr<nsIWebNavigationInfo> info(
@@ -651,22 +680,28 @@ nsObjectLoadingContent::~nsObjectLoading
     // object...
     NS_NOTREACHED("Should not be tearing down a plugin at this point!");
     StopPluginInstance();
   }
   DestroyImageLoadingContent();
 }
 
 nsresult
-nsObjectLoadingContent::InstantiatePluginInstance()
+nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
 {
-  if (mInstanceOwner || mType != eType_Plugin || mIsLoading || mInstantiating) {
+  if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
+      mInstantiating) {
+    // If we hit this assertion it's probably because LoadObject re-entered :(
+    //
+    // XXX(johns): This hackiness will go away in bug 767635
+    NS_ASSERTION(mIsLoading || !aIsLoading,
+                 "aIsLoading should only be true inside LoadObject");
     return NS_OK;
   }
-  
+
   mInstantiating = true;
   AutoSetInstantiatingToFalse autoInstantiating(this);
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
 
   nsIDocument* doc = thisContent->GetCurrentDoc();
   if (!doc || !InActiveDocument(thisContent)) {
@@ -758,16 +793,29 @@ nsObjectLoadingContent::InstantiatePlugi
         nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
                                                      NS_LITERAL_STRING("PluginOutdated"));
         nsresult rv = NS_DispatchToCurrentThread(ev);
         if (NS_FAILED(rv)) {
           NS_WARNING("failed to dispatch nsSimplePluginEvent");
         }
       }
     }
+
+    // If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
+    // or we did load with a channel but are re-instantiating, re-open the
+    // channel. OpenChannel() performs security checks, and this plugin has
+    // already passed content policy in LoadObject.
+    if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
+      NS_ASSERTION(!mChannel, "should not have an existing channel here");
+      if (MakePluginListener()) {
+        // We intentionally ignore errors here, leaving it up to the plugin to
+        // deal with not having an initial stream.
+        OpenChannel();
+      }
+    }
   }
 
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
 {
@@ -783,29 +831,42 @@ nsObjectLoadingContent::NotifyOwnerDocum
   }
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
-  /// This must call LoadObject, even upon failure, to allow it to either
-  /// proceed with the load, or trigger fallback content.
-
   SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest");
 
   LOG(("OBJLC [%p]: Channel OnStartRequest", this));
 
   if (aRequest != mChannel || !aRequest) {
     // happens when a new load starts before the previous one got here
     return NS_BINDING_ABORTED;
   }
 
   NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
+  // If we already switched to type plugin, this channel can just be passed to
+  // the final listener.
+  if (mType == eType_Plugin) {
+    if (!mInstanceOwner || !mFinalListener) {
+      // We drop mChannel when stopping plugins, so something is wrong
+      NS_NOTREACHED("Opened a channel in plugin mode, but don't have a plugin");
+      return NS_BINDING_ABORTED;
+    }
+    return mFinalListener->OnStartRequest(aRequest, nullptr);
+  }
+
+  // Otherwise we should be state loading, and call LoadObject with the channel
+  if (mType != eType_Loading) {
+    NS_NOTREACHED("Should be type loading at this point");
+    return NS_BINDING_ABORTED;
+  }
   NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
 
   mChannelLoaded = true;
 
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   NS_ASSERTION(chan, "Why is our request not a channel?");
 
   nsCOMPtr<nsIURI> uri;
@@ -1621,30 +1682,25 @@ nsObjectLoadingContent::LoadObject(bool 
   }
 
   //
   // Security checks
   //
 
   if (mType != eType_Null) {
     int16_t contentPolicy = nsIContentPolicy::ACCEPT;
-    bool allowLoad = false;
-    // We check load policy before opening a channel, and process policy before
-    // going ahead with any final-type load
-    if (mType == eType_Loading) {
-      nsCOMPtr<nsIScriptSecurityManager> secMan =
-        nsContentUtils::GetSecurityManager();
-      if (!secMan) {
-        NS_NOTREACHED("No security manager?");
-      } else {
-        rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(),
-                                               mURI, 0);
-        allowLoad = NS_SUCCEEDED(rv) && CheckLoadPolicy(&contentPolicy);
-      }
-    } else {
+    bool allowLoad = true;
+    // If mChannelLoaded is set we presumably already passed load policy
+    if (mURI && !mChannelLoaded) {
+      allowLoad = CheckLoadPolicy(&contentPolicy);
+    }
+    // If we're loading a type now, check ProcessPolicy. Note that we may check
+    // both now in the case of plugins whose type is determined before opening a
+    // channel.
+    if (allowLoad && mType != eType_Loading) {
       allowLoad = CheckProcessPolicy(&contentPolicy);
     }
 
     // Content policy implementations can mutate the DOM, check for re-entry
     if (!mIsLoading) {
       LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
            this));
       return NS_OK;
@@ -1710,55 +1766,49 @@ nsObjectLoadingContent::LoadObject(bool 
 
   ///
   /// Attempt to load new type
   ///
 
   // We don't set mFinalListener until OnStartRequest has been called, to
   // prevent re-entry ugliness with CloseChannel()
   nsCOMPtr<nsIStreamListener> finalListener;
+  // If we decide to synchronously spawn a plugin, we do it after firing
+  // notifications to avoid re-entry causing notifications to fire out of order.
+  bool doSpawnPlugin = false;
   switch (mType) {
     case eType_Image:
       if (!mChannel) {
         // We have a LoadImage() call, but UpdateObjectParameters requires a
         // channel for images, so this is not a valid state.
         NS_NOTREACHED("Attempting to load image without a channel?");
         rv = NS_ERROR_UNEXPECTED;
         break;
       }
       rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
       // finalListener will receive OnStartRequest below
     break;
     case eType_Plugin:
     {
       if (mChannel) {
-        nsRefPtr<nsPluginHost> pluginHost =
-          already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
-        if (!pluginHost) {
-          NS_NOTREACHED("No pluginHost");
-          rv = NS_ERROR_UNEXPECTED;
-          break;
-        }
-
         // Force a sync state change now, we need the frame created
         NotifyStateChanged(oldType, oldState, true, aNotify);
         oldType = mType;
         oldState = ObjectState();
 
         if (!thisContent->GetPrimaryFrame()) {
           // We're un-rendered, and can't instantiate a plugin. HasNewFrame will
           // re-start us when we can proceed.
           LOG(("OBJLC [%p]: Aborting load - plugin-type, but no frame", this));
           CloseChannel();
           break;
         }
 
-        rv = pluginHost->NewEmbeddedPluginStreamListener(mURI, this, nullptr,
-                                                         getter_AddRefs(finalListener));
-        // finalListener will receive OnStartRequest below
+        // We'll handle this below
+        doSpawnPlugin = true;
       } else {
         rv = AsyncStartPluginInstance();
       }
     }
     break;
     case eType_Document:
     {
       if (!mChannel) {
@@ -1843,61 +1893,69 @@ nsObjectLoadingContent::LoadObject(bool 
     NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
                  "switched to type null but also loaded something");
 
     if (mChannel) {
       // If we were loading with a channel but then failed over, throw it away
       CloseChannel();
     }
 
-    // Don't try to initialize final listener below
+    // Don't try to initialize plugins or final listener below
+    doSpawnPlugin = false;
     finalListener = nullptr;
 
     // Don't notify, as LoadFallback doesn't know of our previous state
     // (so really this is just setting mFallbackType)
     LoadFallback(fallbackType, false);
   }
 
   // Notify of our final state
   NotifyStateChanged(oldType, oldState, false, aNotify);
+  NS_ENSURE_TRUE(mIsLoading, NS_OK);
+
 
   //
-  // Pass load on to finalListener if loading with a channel.
+  // Spawning plugins and dispatching to the final listener may re-enter, so are
+  // delayed until after we fire a notification, to prevent missing
+  // notifications or firing them out of order.
   //
-  // We do this after finishing our notification such that re-entrance doesn't
-  // skip notifications or fire them out of order.
+  // Note that we ensured that we entered into LoadObject() from
+  // ::OnStartRequest above when loading with a channel.
   //
 
-  if (!mIsLoading) {
-    LOG(("OBJLC [%p]: Re-entered before dispatching to final listener", this));
+  rv = NS_OK;
+  if (doSpawnPlugin) {
+    rv = InstantiatePluginInstance(true);
+    NS_ENSURE_TRUE(mIsLoading, NS_OK);
+    // Create the final listener if we're loading with a channel. We can't do
+    // this in the loading block above as it requires an instance.
+    if (aLoadingChannel && NS_SUCCEEDED(rv)) {
+      // Plugins can continue to run even if their initial stream dies. Some
+      // plugins will even return failure codes to reject the stream, but expect
+      // to continue running, so ignore the error code. rv is thus the result of
+      // spawning the plugin above
+      if (NS_SUCCEEDED(rv) && MakePluginListener()) {
+        mFinalListener->OnStartRequest(mChannel, nullptr);
+      }
+    }
   } else if (finalListener) {
     NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
                  "We should not have a final listener with a non-loaded type");
-    // Note that we always enter into LoadObject() from ::OnStartRequest when
-    // loading with a channel.
-    mSrcStreamLoading = true;
-    // Remove blocker on entering into instantiate
-    // (this is otherwise unset by the stack class)
-    mIsLoading = false;
     mFinalListener = finalListener;
     rv = finalListener->OnStartRequest(mChannel, nullptr);
-    mSrcStreamLoading = false;
-    if (NS_FAILED(rv)) {
-      // Failed to load new content, but since we've already notified of our
-      // transition, we can just Unload and call LoadFallback (which will notify
-      // again)
-      mType = eType_Null;
-      // This could *also* technically re-enter if OnStartRequest fails after
-      // spawning a plugin.
-      mIsLoading = true;
-      UnloadObject(false);
-      NS_ENSURE_TRUE(mIsLoading, NS_OK);
-      CloseChannel();
-      LoadFallback(fallbackType, true);
-    }
+  }
+
+  if (NS_FAILED(rv) && mIsLoading) {
+    // Since we've already notified of our transition, we can just Unload and
+    // call LoadFallback (which will notify again)
+    mType = eType_Null;
+    UnloadObject(false);
+    NS_ENSURE_TRUE(mIsLoading, NS_OK);
+    CloseChannel();
+    LoadFallback(fallbackType, true);
   }
 
   return NS_OK;
 }
 
 // This call can re-enter when dealing with plugin listeners
 nsresult
 nsObjectLoadingContent::CloseChannel()
@@ -1917,32 +1975,35 @@ nsObjectLoadingContent::CloseChannel()
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsObjectLoadingContent::OpenChannel()
 {
-  nsCOMPtr<nsIContent> thisContent = 
+  nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIScriptSecurityManager> secMan =
+    nsContentUtils::GetSecurityManager();
   NS_ASSERTION(thisContent, "must be a content");
   nsIDocument* doc = thisContent->OwnerDoc();
   NS_ASSERTION(doc, "No owner document?");
-  NS_ASSERTION(!mInstanceOwner && !mInstantiating,
-               "opening a new channel with already loaded content");
 
   nsresult rv;
   mChannel = nullptr;
 
   // E.g. mms://
   if (!mURI || !CanHandleURI(mURI)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), mURI, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> chan;
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, rv);
   if (csp) {
     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -111,20 +111,23 @@ class nsObjectLoadingContent : public ns
     ObjectType Type() const { return mType; }
 
     void SetIsNetworkCreated(bool aNetworkCreated)
     {
       mNetworkCreated = aNetworkCreated;
     }
 
     /**
-     * Immediately instantiate a plugin instance. This is a no-op if
-     * mType != eType_Plugin or a plugin is already running.
+     * Immediately instantiate a plugin instance. This is a no-op if mType !=
+     * eType_Plugin or a plugin is already running.
+     *
+     * aIsLoading indicates that we are in the loading code, and we can bypass
+     * the mIsLoading check.
      */
-    nsresult InstantiatePluginInstance();
+    nsresult InstantiatePluginInstance(bool aIsLoading = false);
 
     /**
      * Notify this class the document state has changed
      * Called by nsDocument so we may suspend plugins in inactive documents)
      */
     void NotifyOwnerDocumentActivityChanged();
 
     /**
@@ -175,22 +178,21 @@ class nsObjectLoadingContent : public ns
       eSupportImages       = 1u << 0, // Images are supported (imgILoader)
       eSupportPlugins      = 1u << 1, // Plugins are supported (nsIPluginHost)
       eSupportDocuments    = 1u << 2, // Documents are supported
                                         // (nsIDocumentLoaderFactory)
                                         // This flag always includes SVG
       eSupportSVG          = 1u << 3, // SVG is supported (image/svg+xml)
       eSupportClassID      = 1u << 4, // The classid attribute is supported
 
-      // Allows us to load a plugin if it matches a MIME type or file extension
-      // registered to a plugin without opening its specified URI first. Can
-      // result in launching plugins for URIs that return differing content
-      // types. Plugins without URIs may instantiate regardless.
-      // XXX(johns) this is our legacy behavior on <embed> tags, whereas object
-      // will always open a channel and check its MIME if a URI is present.
+      // If possible to get a *plugin* type from the type attribute *or* file
+      // extension, we can use that type and begin loading the plugin before
+      // opening a channel.
+      // A side effect of this is if the channel fails, the plugin is still
+      // running.
       eAllowPluginSkipChannel  = 1u << 5
     };
 
     /**
      * Returns the list of capabilities this content node supports. This is a
      * bitmask consisting of flags from the Capabilities enum.
      *
      * The default implementation supports all types but not
@@ -325,16 +327,22 @@ class nsObjectLoadingContent : public ns
     /**
      * Checks whether the given type is a supported document type
      *
      * NOTE Does not take content policy or capabilities into account
      */
     bool IsSupportedDocument(const nsCString& aType);
 
     /**
+     * Gets the plugin instance and creates a plugin stream listener, assigning
+     * it to mFinalListener
+     */
+    bool MakePluginListener();
+
+    /**
      * Unloads all content and resets the object to a completely unloaded state
      *
      * NOTE Calls StopPluginInstance() and may spin the event loop
      *
      * @param aResetState Reset the object type to 'loading' and destroy channel
      *                    as well
      */
     void UnloadObject(bool aResetState = true);
@@ -407,18 +415,19 @@ class nsObjectLoadingContent : public ns
 
 
 
     // Type of the currently-loaded content.
     ObjectType                  mType           : 8;
     // The type of fallback content we're showing (see ObjectState())
     FallbackType                mFallbackType : 8;
 
-    // If true, the current load has finished opening a channel. Does not imply
-    // mChannel -- mChannelLoaded && !mChannel may occur for a load that failed
+    // If true, we have opened a channel as the listener and it has reached
+    // OnStartRequest. Does not get set for channels that are passed directly to
+    // the plugin listener.
     bool                        mChannelLoaded    : 1;
 
     // Whether we are about to call instantiate on our frame. If we aren't,
     // SetFrame needs to asynchronously call Instantiate.
     bool                        mInstantiating : 1;
 
     // True when the object is created for an element which the parser has
     // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -3342,25 +3342,24 @@ nsPluginHost::StopPluginInstance(nsNPAPI
     mInstances.RemoveElement(aInstance);
     OnPluginInstanceDestroyed(pluginTag);
   }
 
   return NS_OK;
 }
 
 nsresult nsPluginHost::NewEmbeddedPluginStreamListener(nsIURI* aURI,
-                                                       nsObjectLoadingContent *aContent,
                                                        nsNPAPIPluginInstance* aInstance,
                                                        nsIStreamListener **aStreamListener)
 {
   NS_ENSURE_ARG_POINTER(aURI);
   NS_ENSURE_ARG_POINTER(aStreamListener);
 
   nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
-  nsresult rv = listener->InitializeEmbedded(aURI, aInstance, aContent);
+  nsresult rv = listener->InitializeEmbedded(aURI, aInstance);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   listener.forget(aStreamListener);
 
   return NS_OK;
 }
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -194,17 +194,17 @@ public:
                                              nsPluginInstanceOwner **aOwner,
                                              nsIStreamListener **aStreamListener);
 
   // Does not accept NULL and should never fail.
   nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
 
   nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
 
-  nsresult NewEmbeddedPluginStreamListener(nsIURI* aURL, nsObjectLoadingContent *aContent,
+  nsresult NewEmbeddedPluginStreamListener(nsIURI* aURL,
                                            nsNPAPIPluginInstance* aInstance,
                                            nsIStreamListener **aStreamListener);
 
   nsresult NewFullPagePluginStreamListener(nsIURI* aURI,
                                            nsNPAPIPluginInstance *aInstance,
                                            nsIStreamListener **aStreamListener);
 
 private:
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -22,17 +22,17 @@
 #include "nsPrintfCString.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDocument.h"
 #include "nsIWebNavigation.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsPluginNativeWindow.h"
 #include "sampler.h"
-#include "nsObjectLoadingContent.h"
+#include "nsPluginInstanceOwner.h"
 
 #define MAGIC_REQUEST_CONTEXT 0x01020304
 
 // nsPluginByteRangeStreamListener
 
 class nsPluginByteRangeStreamListener
   : public nsIStreamListener
   , public nsIInterfaceRequestor
@@ -341,49 +341,44 @@ nsresult nsPluginStreamListenerPeer::Ini
   mDataForwardToRequest = new nsHashtable(16, false);
   if (!mDataForwardToRequest)
     return NS_ERROR_FAILURE;
   
   return NS_OK;
 }
 
 nsresult nsPluginStreamListenerPeer::InitializeEmbedded(nsIURI *aURL,
-                                                        nsNPAPIPluginInstance* aInstance,
-                                                        nsObjectLoadingContent *aContent)
+                                                        nsNPAPIPluginInstance* aInstance)
 {
 #ifdef PLUGIN_LOGGING
   nsAutoCString urlSpec;
   aURL->GetSpec(urlSpec);
-  
+
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
          ("nsPluginStreamListenerPeer::InitializeEmbedded url=%s\n", urlSpec.get()));
-  
+
   PR_LogFlush();
 #endif
 
-  // We have to have one or the other.
-  if (!aInstance && !aContent) {
+  // Not gonna work out
+  if (!aInstance) {
     return NS_ERROR_FAILURE;
   }
 
   mURL = aURL;
-  
-  if (aInstance) {
-    NS_ASSERTION(mPluginInstance == nullptr, "nsPluginStreamListenerPeer::InitializeEmbedded mPluginInstance != nullptr");
-    mPluginInstance = aInstance;
-  } else {
-    mContent = aContent;
-  }
-  
+
+  NS_ASSERTION(mPluginInstance == nullptr, "nsPluginStreamListenerPeer::InitializeEmbedded mPluginInstance != nullptr");
+  mPluginInstance = aInstance;
+
   mPendingRequests = 1;
-  
+
   mDataForwardToRequest = new nsHashtable(16, false);
   if (!mDataForwardToRequest)
     return NS_ERROR_FAILURE;
-  
+
   return NS_OK;
 }
 
 // Called by NewFullPagePluginStream()
 nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance)
 {
   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
              ("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
@@ -492,41 +487,41 @@ nsPluginStreamListenerPeer::OnStartReque
   nsresult rv = NS_OK;
   SAMPLE_LABEL("nsPluginStreamListenerPeer", "OnStartRequest");
 
   if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) {
     NS_ASSERTION(mRequests.Count() == 0,
                  "Only our initial stream should be unknown!");
     TrackRequest(request);
   }
-  
+
   if (mHaveFiredOnStartRequest) {
     return NS_OK;
   }
-  
+
   mHaveFiredOnStartRequest = true;
-  
+
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
-  
+
   // deal with 404 (Not Found) HTTP response,
   // just return, this causes the request to be ignored.
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     uint32_t responseCode = 0;
     rv = httpChannel->GetResponseStatus(&responseCode);
     if (NS_FAILED(rv)) {
       // NPP_Notify() will be called from OnStopRequest
       // in nsNPAPIPluginStreamListener::CleanUpStream
       // return error will cancel this request
       // ...and we also need to tell the plugin that
       mRequestFailed = true;
       return NS_ERROR_FAILURE;
     }
-    
+
     if (responseCode > 206) { // not normal
       bool bWantsAllNetworkStreams = false;
 
       // We don't always have an instance here already, but if we do, check
       // to see if it wants all streams.
       if (mPluginInstance) {
         rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
                                                  &bWantsAllNetworkStreams);
@@ -537,90 +532,76 @@ nsPluginStreamListenerPeer::OnStartReque
       }
 
       if (!bWantsAllNetworkStreams) {
         mRequestFailed = true;
         return NS_ERROR_FAILURE;
       }
     }
   }
-  
+
   // Get the notification callbacks from the channel and save it as
   // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
   // we'll create channel for byte range request.
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
   if (callbacks)
     mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
-  
+
   nsCOMPtr<nsILoadGroup> loadGroup;
   channel->GetLoadGroup(getter_AddRefs(loadGroup));
   if (loadGroup)
     mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
-  
+
   int64_t length;
   rv = channel->GetContentLength(&length);
-  
+
   // it's possible for the server to not send a Content-Length.
   // we should still work in this case.
   if (NS_FAILED(rv) || length == -1) {
     // check out if this is file channel
     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
     if (fileChannel) {
       // file does not exist
       mRequestFailed = true;
       return NS_ERROR_FAILURE;
     }
     mLength = 0;
   }
   else {
     mLength = length;
   }
-  
+
   nsAutoCString aContentType; // XXX but we already got the type above!
   rv = channel->GetContentType(aContentType);
   if (NS_FAILED(rv))
     return rv;
-  
+
   nsCOMPtr<nsIURI> aURL;
   rv = channel->GetURI(getter_AddRefs(aURL));
   if (NS_FAILED(rv))
     return rv;
-  
+
   aURL->GetSpec(mURLSpec);
-  
+
   if (!aContentType.IsEmpty())
     mContentType = aContentType;
-  
+
 #ifdef PLUGIN_LOGGING
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
          ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
           this, request, aContentType.get(), mURLSpec.get()));
-  
+
   PR_LogFlush();
 #endif
 
-  // If we don't have an instance yet it means we weren't able to load
-  // a plugin previously because we didn't have the mimetype. Try again
-  // if we have a mime type now.
-  if (!mPluginInstance && mContent && !aContentType.IsEmpty()) {
-    nsObjectLoadingContent *olc = static_cast<nsObjectLoadingContent*>(mContent.get());
-    rv = olc->InstantiatePluginInstance();
-    if (NS_SUCCEEDED(rv)) {
-      rv = olc->GetPluginInstance(getter_AddRefs(mPluginInstance));
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
-    }
-  }
-
   // Set up the stream listener...
   rv = SetUpStreamListener(request, aURL);
   if (NS_FAILED(rv)) return rv;
-  
+
   return rv;
 }
 
 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
                                                      nsISupports* aContext,
                                                      uint64_t aProgress,
                                                      uint64_t aProgressMax)
 {
--- a/dom/plugins/base/nsPluginStreamListenerPeer.h
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.h
@@ -12,20 +12,18 @@
 #include "nsIProgressEventSink.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsWeakReference.h"
 #include "nsNPAPIPluginStreamListener.h"
 #include "nsHashtable.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
-#include "nsIObjectLoadingContent.h"
 
 class nsIChannel;
-class nsObjectLoadingContent;
 
 /**
  * When a plugin requests opens multiple requests to the same URL and
  * the request must be satified by saving a file to disk, each stream
  * listener holds a reference to the backing file: the file is only removed
  * when all the listeners are done.
  */
 class CachedFileHolder
@@ -70,18 +68,17 @@ public:
   bool UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi);
   
   // Called by GetURL and PostURL (via NewStream)
   nsresult Initialize(nsIURI *aURL,
                       nsNPAPIPluginInstance *aInstance,
                       nsNPAPIPluginStreamListener *aListener);
   
   nsresult InitializeEmbedded(nsIURI *aURL,
-                              nsNPAPIPluginInstance* aInstance,
-                              nsObjectLoadingContent *aContent);
+                              nsNPAPIPluginInstance* aInstance);
   
   nsresult InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance);
 
   nsresult OnFileAvailable(nsIFile* aFile);
   
   nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
   
   nsNPAPIPluginInstance *GetPluginInstance() { return mPluginInstance; }
@@ -135,17 +132,16 @@ public:
 
 private:
   nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
   nsresult SetupPluginCacheFile(nsIChannel* channel);
   nsresult GetInterfaceGlobal(const nsIID& aIID, void** result);
 
   nsCOMPtr<nsIURI> mURL;
   nsCString mURLSpec; // Have to keep this member because GetURL hands out char*
-  nsCOMPtr<nsIObjectLoadingContent> mContent;
   nsRefPtr<nsNPAPIPluginStreamListener> mPStreamListener;
 
   // Set to true if we request failed (like with a HTTP response of 404)
   bool                    mRequestFailed;
   
   /*
    * Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
    * been called.  Checked in ::OnStopRequest so we can call the