Backed out changeset 77dc38d8196e - bug 548217 because even though this patch is correct, it exposes a bug in the OOPP code which got backed out.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Thu, 25 Feb 2010 03:57:57 -0800
changeset 38693 3fa8aa3c951ce7b1c604cc764ff2f85536a88a6e
parent 38667 77dc38d8196e827f0099ce97b1abffd9de408644
child 38694 059e9961a122a804956918a2c9546da106a3046a
push idunknown
push userunknown
push dateunknown
bugs548217
milestone1.9.3a2pre
backs out77dc38d8196e827f0099ce97b1abffd9de408644
Backed out changeset 77dc38d8196e - bug 548217 because even though this patch is correct, it exposes a bug in the OOPP code which got backed out.
modules/plugin/base/public/nsIPluginInstanceOwner.idl
modules/plugin/base/src/nsNPAPIPlugin.cpp
modules/plugin/base/src/nsNPAPIPluginInstance.cpp
modules/plugin/base/src/nsPluginHost.cpp
modules/plugin/test/testplugin/nptest.cpp
--- a/modules/plugin/base/public/nsIPluginInstanceOwner.idl
+++ b/modules/plugin/base/public/nsIPluginInstanceOwner.idl
@@ -58,23 +58,16 @@ interface nsIPluginInstanceOwner : nsISu
    */
   void setInstance(in nsIPluginInstance aInstance);
 
   /**
    * Get the instance associated with this owner.
    */
   void getInstance(in nsIPluginInstanceRef aInstance);
 
-%{C++
-  // make getter_AddRefs work
-  inline nsresult GetInstance(nsIPluginInstance** aInstance) {
-    return GetInstance(*aInstance);
-  }
-%}
-
   /**
    * Get a handle to the window structure of the owner.
    * This pointer cannot be made persistent by the caller.
    */
   void getWindow(in NPWindowStarRef aWindow);
 
   /**
    * Get the display mode for the plugin instance.
--- a/modules/plugin/base/src/nsNPAPIPlugin.cpp
+++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp
@@ -2374,23 +2374,18 @@ NPError NP_CALLBACK
 
   PRInt32 streamtype = NP_NORMAL;
 
   streamlistener->GetStreamType(&streamtype);
 
   if (streamtype != NP_SEEK)
     return NPERR_STREAM_NOT_SEEKABLE;
 
-  if (!streamlistener->mStreamInfo)
-    return NPERR_GENERIC_ERROR;
-
-  nsresult rv = streamlistener->mStreamInfo
-    ->RequestRead((NPByteRange *)rangeList);
-  if (NS_FAILED(rv))
-    return NPERR_GENERIC_ERROR;
+  if (streamlistener->mStreamInfo)
+    streamlistener->mStreamInfo->RequestRead((NPByteRange *)rangeList);
 
   return NS_OK;
 }
 
 // Deprecated, only stubbed out
 void* NP_CALLBACK /* OJI type: JRIEnv* */
 _getJavaEnv()
 {
--- a/modules/plugin/base/src/nsNPAPIPluginInstance.cpp
+++ b/modules/plugin/base/src/nsNPAPIPluginInstance.cpp
@@ -251,30 +251,19 @@ nsNPAPIPluginStreamListener::~nsNPAPIPlu
 
 nsresult nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason)
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   if (mStreamCleanedUp)
     return NS_OK;
 
-  mStreamCleanedUp = PR_TRUE;
-
-  StopDataPump();
-
-  // Seekable streams have an extra addref when they are created which must
-  // be matched here.
-  if (NP_SEEK == mStreamType)
-    NS_RELEASE_THIS();
-
   if (!mInst || !mInst->IsRunning())
     return rv;
 
-  mStreamInfo = NULL;
-
   PluginDestructionGuard guard(mInst);
 
   const NPPluginFuncs *callbacks = nsnull;
   mInst->GetCallbacks(&callbacks);
   if (!callbacks)
     return rv;
 
   NPP npp;
@@ -291,17 +280,20 @@ nsresult nsNPAPIPluginStreamListener::Cl
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
     this, npp, reason, error, mNPStream.url));
 
     if (error == NPERR_NO_ERROR)
       rv = NS_OK;
   }
 
-  mStreamStarted = PR_FALSE;
+  mStreamCleanedUp = PR_TRUE;
+  mStreamStarted   = PR_FALSE;
+
+  StopDataPump();
 
   // fire notification back to plugin, just like before
   CallURLNotify(reason);
 
   return rv;
 }
 
 void nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason)
@@ -388,22 +380,16 @@ nsNPAPIPluginStreamListener::OnStartBind
     case NP_ASFILEONLY:
       mStreamType = NP_ASFILEONLY; 
       break;
     case NP_ASFILE:
       mStreamType = NP_ASFILE; 
       break;
     case NP_SEEK:
       mStreamType = NP_SEEK; 
-      // Seekable streams should continue to exist even after OnStopRequest
-      // is fired, so we AddRef ourself an extra time and Release when the
-      // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never
-      // calls NPN_DestroyStream the stream will be destroyed before the plugin
-      // instance is destroyed.
-      NS_ADDREF_THIS();
       break;
     default:
       return NS_ERROR_FAILURE;
   }
 
   mStreamStarted = PR_TRUE;
   return NS_OK;
 }
@@ -803,23 +789,29 @@ nsNPAPIPluginStreamListener::OnStopBindi
   }
 
   if (!mInst || !mInst->IsRunning())
     return NS_ERROR_FAILURE;
 
   // check if the stream is of seekable type and later its destruction
   // see bug 91140    
   nsresult rv = NS_OK;
-  NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
-  if (mStreamType != NP_SEEK ||
-      (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) {
+  if (mStreamType != NP_SEEK) {
+    NPReason reason = NPRES_DONE;
+
+    if (NS_FAILED(status))
+      reason = NPRES_NETWORK_ERR;   // since the stream failed, we need to tell the plugin that
+
     rv = CleanUpStream(reason);
   }
 
-  return rv;
+  if (rv != NPERR_NO_ERROR)
+    return NS_ERROR_FAILURE;
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginStreamListener::GetStreamType(PRInt32 *result)
 {
   *result = mStreamType;
   return NS_OK;
 }
@@ -1014,17 +1006,17 @@ NS_IMETHODIMP nsNPAPIPluginInstance::Sto
   if (mCallbacks->destroy == NULL) {
     return NS_ERROR_FAILURE;
   }
 
   NPSavedData *sdata = 0;
 
   // clean up open streams
   for (nsInstanceStream *is = mStreams; is != nsnull;) {
-    nsRefPtr<nsNPAPIPluginStreamListener> listener = is->mPluginStreamListener;
+    nsNPAPIPluginStreamListener * listener = is->mPluginStreamListener;
 
     nsInstanceStream *next = is->mNext;
     delete is;
     is = next;
     mStreams = is;
 
     // Clean up our stream after removing it from the list because 
     // it may be released and destroyed at this point.
--- a/modules/plugin/base/src/nsPluginHost.cpp
+++ b/modules/plugin/base/src/nsPluginHost.cpp
@@ -133,17 +133,17 @@
 #include "nsPluginDirServiceProvider.h"
 #include "nsInt64.h"
 #include "nsPluginError.h"
 
 #include "nsUnicharUtils.h"
 #include "nsPluginManifestLineReader.h"
 
 #include "nsDefaultPlugin.h"
-#include "nsIWeakReferenceUtils.h"
+#include "nsWeakReference.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLObjectElement.h"
 #include "nsIDOMHTMLEmbedElement.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIWebNavigation.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
@@ -340,41 +340,118 @@ nsresult nsPluginHost::PostPluginUnloadE
     return NS_OK;
 
   // failure case
   NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(aLibrary), nsnull, nsnull);
 
   return NS_ERROR_FAILURE;
 }
 
+class nsPluginStreamListenerPeer;
+
+class nsPluginStreamInfo : public nsINPAPIPluginStreamInfo
+{
+public:
+  nsPluginStreamInfo();
+  virtual ~nsPluginStreamInfo();
+
+  NS_DECL_ISUPPORTS
+
+  // nsINPAPIPluginStreamInfo interface
+ 
+  NS_IMETHOD
+  GetContentType(char **result);
+
+  NS_IMETHOD
+  IsSeekable(PRBool* result);
+
+  NS_IMETHOD
+  GetLength(PRUint32* result);
+
+  NS_IMETHOD
+  GetLastModified(PRUint32* result);
+
+  NS_IMETHOD
+  GetURL(const char** result);
+
+  NS_IMETHOD
+  RequestRead(NPByteRange* rangeList);
+
+  NS_IMETHOD
+  GetStreamOffset(PRInt32 *result);
+
+  NS_IMETHOD
+  SetStreamOffset(PRInt32 result);
+
+  // local methods
+
+  void
+  SetContentType(const char* contentType);
+
+  void
+  SetSeekable(const PRBool seekable);
+
+  void
+  SetLength(const PRUint32 length);
+
+  void
+  SetLastModified(const PRUint32 modified);
+
+  void
+  SetURL(const char* url);
+
+  void
+  SetPluginInstance(nsIPluginInstance * aPluginInstance);
+
+  void
+  SetPluginStreamListenerPeer(nsPluginStreamListenerPeer * aPluginStreamListenerPeer);
+
+  void
+  MakeByteRangeString(NPByteRange* aRangeList, nsACString &string, PRInt32 *numRequests);
+
+  PRBool
+  UseExistingPluginCacheFile(nsPluginStreamInfo* psi);
+
+  void
+  SetStreamComplete(const PRBool complete);
+
+  void
+  SetRequest(nsIRequest *request)
+  {
+    mRequest = request;
+  }
+
+private:
+
+  char* mContentType;
+  char* mURL;
+  PRBool mSeekable;
+  PRUint32 mLength;
+  PRUint32 mModified;
+  nsIPluginInstance * mPluginInstance;
+  nsPluginStreamListenerPeer * mPluginStreamListenerPeer;
+  PRInt32 mStreamOffset;
+  PRBool mStreamComplete;
+};
+
 class nsPluginStreamListenerPeer : public nsIStreamListener,
                                    public nsIProgressEventSink,
                                    public nsIHttpHeaderVisitor,
-                                   public nsSupportsWeakReference,
-                                   public nsINPAPIPluginStreamInfo
+                                   public nsSupportsWeakReference
 {
 public:
   nsPluginStreamListenerPeer();
   virtual ~nsPluginStreamListenerPeer();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPROGRESSEVENTSINK
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIHTTPHEADERVISITOR
 
-  // nsINPAPIPluginStreamInfo interface
-  NS_DECL_NSIPLUGINSTREAMINFO
-
-  // Called by RequestRead
-  void
-  MakeByteRangeString(NPByteRange* aRangeList, nsACString &string, PRInt32 *numRequests);
-
-  PRBool UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi);
-
   // Called by GetURL and PostURL (via NewStream)
   nsresult Initialize(nsIURI *aURL,
                       nsIPluginInstance *aInstance,
                       nsIPluginStreamListener *aListener,
                       PRInt32 requestCount = 1);
 
   nsresult InitializeEmbedded(nsIURI *aURL,
                              nsIPluginInstance* aInstance,
@@ -387,50 +464,44 @@ public:
   nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
   
   nsIPluginInstance *GetPluginInstance() { return mInstance; }
 
 private:
   nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
   nsresult SetupPluginCacheFile(nsIChannel* channel);
 
-  nsCOMPtr<nsIURI> mURL;
-  nsCString mURLSpec; // Have to keep this member because GetURL hands out char*
-  nsCOMPtr<nsIPluginInstanceOwner> mOwner;
-  nsCOMPtr<nsIPluginInstance> mInstance;
-  nsCOMPtr<nsIPluginStreamListener> mPStreamListener;
+  nsIURI                  *mURL;
+  nsIPluginInstanceOwner  *mOwner;
+  nsIPluginInstance       *mInstance;
+  nsIPluginStreamListener *mPStreamListener;
+  nsRefPtr<nsPluginStreamInfo> mPluginStreamInfo;
 
   // Set to PR_TRUE if we request failed (like with a HTTP response of 404)
   PRPackedBool            mRequestFailed;
 
   /*
    * Set to PR_TRUE after nsIPluginStreamListener::OnStartBinding() has
    * been called.  Checked in ::OnStopRequest so we can call the
    * plugin's OnStartBinding if, for some reason, it has not already
    * been called.
    */
   PRPackedBool      mStartBinding;
   PRPackedBool      mHaveFiredOnStartRequest;
   // these get passed to the plugin stream listener
+  char                    *mMIMEType;
   PRUint32                mLength;
   PRInt32                 mStreamType;
 
   // local cached file, we save the content into local cache if browser cache is not available,
   // or plugin asks stream as file and it expects file extension until bug 90558 got fixed
-  nsCOMPtr<nsIFile> mLocalCachedFile;
+  nsIFile                 *mLocalCachedFile;
   nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
   nsHashtable             *mDataForwardToRequest;
 
-  nsCString mContentType;
-  PRBool mSeekable;
-  PRUint32 mModified;
-  nsCOMPtr<nsIPluginInstance> mPluginInstance;
-  PRInt32 mStreamOffset;
-  PRBool mStreamComplete;
-
 public:
   PRBool                  mAbort;
   PRInt32                 mPendingRequests;
   nsWeakPtr               mWeakPtrChannelCallbacks;
   nsWeakPtr               mWeakPtrChannelLoadGroup;
 };
 
 class nsPluginByteRangeStreamListener : public nsIStreamListener {
@@ -443,54 +514,80 @@ public:
   NS_DECL_NSISTREAMLISTENER
 
 private:
   nsCOMPtr<nsIStreamListener> mStreamConverter;
   nsWeakPtr mWeakPtrPluginStreamListenerPeer;
   PRBool mRemoveMagicNumber;
 };
 
+nsPluginStreamInfo::nsPluginStreamInfo()
+{
+  mPluginInstance = nsnull;
+  mPluginStreamListenerPeer = nsnull;
+
+  mContentType = nsnull;
+  mURL = nsnull;
+  mSeekable = PR_FALSE;
+  mLength = 0;
+  mModified = 0;
+  mStreamOffset = 0;
+  mStreamComplete = PR_FALSE;
+}
+
+nsPluginStreamInfo::~nsPluginStreamInfo()
+{
+  if (mContentType)
+    PL_strfree(mContentType);
+  if (mURL)
+    PL_strfree(mURL);
+
+  NS_IF_RELEASE(mPluginInstance);
+}
+
+NS_IMPL_ISUPPORTS2(nsPluginStreamInfo, nsIPluginStreamInfo,
+                   nsINPAPIPluginStreamInfo)
+
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::GetContentType(char** result)
+nsPluginStreamInfo::GetContentType(char **result)
 {
-  *result = const_cast<char*>(mContentType.get());
+  *result = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::IsSeekable(PRBool* result)
+nsPluginStreamInfo::IsSeekable(PRBool* result)
 {
   *result = mSeekable;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::GetLength(PRUint32* result)
+nsPluginStreamInfo::GetLength(PRUint32* result)
 {
   *result = mLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::GetLastModified(PRUint32* result)
+nsPluginStreamInfo::GetLastModified(PRUint32* result)
 {
   *result = mModified;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::GetURL(const char** result)
+nsPluginStreamInfo::GetURL(const char** result)
 {
-  *result = mURLSpec.get();
+  *result = mURL;
   return NS_OK;
 }
 
 void
-nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest,
-                                                PRInt32 *numRequests)
+nsPluginStreamInfo::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest, PRInt32 *numRequests)
 {
   rangeRequest.Truncate();
   *numRequests  = 0;
   //the string should look like this: bytes=500-700,601-999
   if (!aRangeList)
     return;
 
   PRInt32 requestCnt = 0;
@@ -515,87 +612,149 @@ nsPluginStreamListenerPeer::MakeByteRang
   string.Trim(",", PR_FALSE);
 
   rangeRequest = string;
   *numRequests  = requestCnt;
   return;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
+nsPluginStreamInfo::RequestRead(NPByteRange* rangeList)
 {
   nsCAutoString rangeString;
   PRInt32 numRequests;
 
+  //first of all lets see if mPluginStreamListenerPeer is still alive
+  nsCOMPtr<nsISupportsWeakReference> suppWeakRef(
+    do_QueryInterface((nsISupportsWeakReference *)(mPluginStreamListenerPeer)));
+  if (!suppWeakRef)
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIWeakReference> pWeakRefPluginStreamListenerPeer =
+           do_GetWeakReference(suppWeakRef);
+  if (!pWeakRefPluginStreamListenerPeer)
+    return NS_ERROR_FAILURE;
+
   MakeByteRangeString(rangeList, rangeString, &numRequests);
 
   if (numRequests == 0)
     return NS_ERROR_FAILURE;
 
   nsresult rv = NS_OK;
-
-  nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
-  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
+  nsCOMPtr<nsIURI> url;
+
+  rv = NS_NewURI(getter_AddRefs(url), nsDependentCString(mURL));
+
+  nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mPluginStreamListenerPeer->mWeakPtrChannelCallbacks);
+  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mPluginStreamListenerPeer->mWeakPtrChannelLoadGroup);
   nsCOMPtr<nsIChannel> channel;
-  rv = NS_NewChannel(getter_AddRefs(channel), mURL, nsnull, loadGroup, callbacks);
+  rv = NS_NewChannel(getter_AddRefs(channel), url, nsnull, loadGroup, callbacks);
   if (NS_FAILED(rv))
     return rv;
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (!httpChannel)
     return NS_ERROR_FAILURE;
 
   httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
 
-  mAbort = PR_TRUE; // instruct old stream listener to cancel
-                    // the request on the next ODA.
+  mPluginStreamListenerPeer->mAbort = PR_TRUE; // instruct old stream listener to cancel
+                                               // the request on the next ODA.
 
   nsCOMPtr<nsIStreamListener> converter;
 
   if (numRequests == 1) {
-    converter = this;
+    converter = mPluginStreamListenerPeer;
+
     // set current stream offset equal to the first offset in the range list
     // it will work for single byte range request
     // for multy range we'll reset it in ODA
     SetStreamOffset(rangeList->offset);
   } else {
-    nsWeakPtr weakpeer =
-      do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
     nsPluginByteRangeStreamListener *brrListener =
-      new nsPluginByteRangeStreamListener(weakpeer);
+      new nsPluginByteRangeStreamListener(pWeakRefPluginStreamListenerPeer);
     if (brrListener)
       converter = brrListener;
     else
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  mPendingRequests += numRequests;
+  mPluginStreamListenerPeer->mPendingRequests += numRequests;
 
   nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
   if (NS_FAILED(rv))
     return rv;
   rv = container->SetData(MAGIC_REQUEST_CONTEXT);
   if (NS_FAILED(rv))
     return rv;
 
   return channel->AsyncOpen(converter, container);
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::GetStreamOffset(PRInt32* result)
+nsPluginStreamInfo::GetStreamOffset(PRInt32 *result)
 {
   *result = mStreamOffset;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginStreamListenerPeer::SetStreamOffset(PRInt32 value)
+nsPluginStreamInfo::SetStreamOffset(PRInt32 offset)
+{
+  mStreamOffset = offset;
+  return NS_OK;
+}
+
+void
+nsPluginStreamInfo::SetContentType(const char* contentType)
+{
+  if (mContentType != nsnull)
+    PL_strfree(mContentType);
+
+  mContentType = PL_strdup(contentType);
+}
+
+void
+nsPluginStreamInfo::SetSeekable(const PRBool seekable)
+{
+  mSeekable = seekable;
+}
+
+void
+nsPluginStreamInfo::SetLength(const PRUint32 length)
 {
-  mStreamOffset = value;
-  return NS_OK;
+  mLength = length;
+}
+
+void
+nsPluginStreamInfo::SetLastModified(const PRUint32 modified)
+{
+  mModified = modified;
+}
+
+void
+nsPluginStreamInfo::SetURL(const char* url)
+{
+  if (mURL)
+    PL_strfree(mURL);
+
+  mURL = PL_strdup(url);
+}
+
+void
+nsPluginStreamInfo::SetPluginInstance(nsIPluginInstance * aPluginInstance)
+{
+  NS_IF_ADDREF(mPluginInstance = aPluginInstance);
+}
+
+void
+nsPluginStreamInfo::SetPluginStreamListenerPeer(nsPluginStreamListenerPeer * aPluginStreamListenerPeer)
+{
+  // not addref'd - nsPluginStreamInfo is owned by mPluginStreamListenerPeer
+  mPluginStreamListenerPeer = aPluginStreamListenerPeer;
 }
 
 class nsPluginCacheListener : public nsIStreamListener
 {
 public:
   nsPluginCacheListener(nsPluginStreamListenerPeer* aListener);
   virtual ~nsPluginCacheListener();
 
@@ -654,38 +813,46 @@ nsPluginCacheListener::OnStopRequest(nsI
                                      nsISupports* aContext,
                                      nsresult aStatus)
 {
   return NS_OK;
 }
 
 nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
 {
+  mURL = nsnull;
+  mOwner = nsnull;
+  mInstance = nsnull;
+  mPStreamListener = nsnull;
   mStreamType = NP_NORMAL;
   mStartBinding = PR_FALSE;
   mAbort = PR_FALSE;
   mRequestFailed = PR_FALSE;
 
   mPendingRequests = 0;
   mHaveFiredOnStartRequest = PR_FALSE;
   mDataForwardToRequest = nsnull;
-
-  mSeekable = PR_FALSE;
-  mModified = 0;
-  mStreamOffset = 0;
-  mStreamComplete = 0;
+  mLocalCachedFile = nsnull;
 }
 
 nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
 {
 #ifdef PLUGIN_LOGGING
+  nsCAutoString urlSpec;
+  if (mURL != nsnull) mURL->GetSpec(urlSpec);
+
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
-    ("nsPluginStreamListenerPeer::dtor this=%p, url=%s%c",this, mURLSpec.get(), mLocalCachedFile?',':'\n'));
+    ("nsPluginStreamListenerPeer::dtor this=%p, url=%s%c",this, urlSpec.get(), mLocalCachedFile?',':'\n'));
 #endif
 
+  NS_IF_RELEASE(mURL);
+  NS_IF_RELEASE(mOwner);
+  NS_IF_RELEASE(mInstance);
+  NS_IF_RELEASE(mPStreamListener);
+
   // close FD of mFileCacheOutputStream if it's still open
   // or we won't be able to remove the cache file
   if (mFileCacheOutputStream)
     mFileCacheOutputStream = nsnull;
 
   // if we have mLocalCachedFile lets release it
   // and it'll be fiscally remove if refcnt == 1
   if (mLocalCachedFile) {
@@ -697,29 +864,28 @@ nsPluginStreamListenerPeer::~nsPluginStr
     mLocalCachedFile->GetNativePath(filePath);
 
     PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
       ("LocalyCachedFile=%s has %d refcnt and will %s be deleted now\n",filePath.get(),refcnt,refcnt==1?"":"NOT"));
 #endif
 
     if (refcnt == 1) {
       mLocalCachedFile->Remove(PR_FALSE);
+      NS_RELEASE(mLocalCachedFile);
     }
   }
 
   delete mDataForwardToRequest;
 }
 
-NS_IMPL_ISUPPORTS6(nsPluginStreamListenerPeer,
+NS_IMPL_ISUPPORTS4(nsPluginStreamListenerPeer,
                    nsIStreamListener,
                    nsIRequestObserver,
                    nsIHttpHeaderVisitor,
-                   nsISupportsWeakReference,
-                   nsIPluginStreamInfo,
-                   nsINPAPIPluginStreamInfo)
+                   nsISupportsWeakReference)
 
 // Called as a result of GetURL and PostURL
 nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
                                                 nsIPluginInstance *aInstance,
                                                 nsIPluginStreamListener* aListener,
                                                 PRInt32 requestCount)
 {
 #ifdef PLUGIN_LOGGING
@@ -728,19 +894,30 @@ nsresult nsPluginStreamListenerPeer::Ini
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
         ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
 
   PR_LogFlush();
 #endif
 
   mURL = aURL;
+  NS_ADDREF(mURL);
 
   mInstance = aInstance;
+  NS_ADDREF(mInstance);
+
   mPStreamListener = aListener;
+  NS_ADDREF(mPStreamListener);
+
+  mPluginStreamInfo = new nsPluginStreamInfo();
+  if (!mPluginStreamInfo)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mPluginStreamInfo->SetPluginInstance(aInstance);
+  mPluginStreamInfo->SetPluginStreamListenerPeer(this);
 
   mPendingRequests = requestCount;
 
   mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
   if (!mDataForwardToRequest)
       return NS_ERROR_FAILURE;
 
   return NS_OK;
@@ -762,40 +939,58 @@ nsresult nsPluginStreamListenerPeer::Ini
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
         ("nsPluginStreamListenerPeer::InitializeEmbedded url=%s\n", urlSpec.get()));
 
   PR_LogFlush();
 #endif
 
   mURL = aURL;
+  NS_ADDREF(mURL);
 
   if (aInstance) {
     NS_ASSERTION(mInstance == nsnull, "nsPluginStreamListenerPeer::InitializeEmbedded mInstance != nsnull");
     mInstance = aInstance;
+    NS_ADDREF(mInstance);
   } else {
     mOwner = aOwner;
+    NS_IF_ADDREF(mOwner);
   }
 
+  mPluginStreamInfo = new nsPluginStreamInfo();
+  if (!mPluginStreamInfo)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mPluginStreamInfo->SetPluginInstance(aInstance);
+  mPluginStreamInfo->SetPluginStreamListenerPeer(this);
+
   mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
   if (!mDataForwardToRequest)
       return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
 
 // Called by NewFullPagePluginStream()
 nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIPluginInstance *aInstance)
 {
   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   ("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
 
   NS_ASSERTION(mInstance == nsnull, "nsPluginStreamListenerPeer::InitializeFullPage mInstance != nsnull");
   mInstance = aInstance;
+  NS_ADDREF(mInstance);
+
+  mPluginStreamInfo = new nsPluginStreamInfo();
+  if (!mPluginStreamInfo)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mPluginStreamInfo->SetPluginInstance(aInstance);
+  mPluginStreamInfo->SetPluginStreamListenerPeer(this);
 
   mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
   if (!mDataForwardToRequest)
       return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
@@ -819,20 +1014,21 @@ nsPluginStreamListenerPeer::SetupPluginC
     nsPluginInstanceTag *instanceTag = (*instanceTags)[i];
     if (instanceTag->mStreams) {
       // most recent streams are at the end of list
       PRInt32 cnt;
       instanceTag->mStreams->Count((PRUint32*)&cnt);
       while (--cnt >= 0) {
         nsPluginStreamListenerPeer *lp =
           reinterpret_cast<nsPluginStreamListenerPeer*>(instanceTag->mStreams->ElementAt(cnt));
-        if (lp && lp->mLocalCachedFile) {
-          useExistingCacheFile = lp->UseExistingPluginCacheFile(this);
+        if (lp && lp->mLocalCachedFile && lp->mPluginStreamInfo) {
+          useExistingCacheFile = lp->mPluginStreamInfo->UseExistingPluginCacheFile(mPluginStreamInfo);
           if (useExistingCacheFile) {
             mLocalCachedFile = lp->mLocalCachedFile;
+            NS_ADDREF(mLocalCachedFile);
             break;
           }
           NS_RELEASE(lp);
         }
       }
       if (useExistingCacheFile)
         break;
     }
@@ -872,19 +1068,18 @@ nsPluginStreamListenerPeer::SetupPluginC
 
     // create a file output stream to write to...
     nsCOMPtr<nsIOutputStream> outstream;
     rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
     if (NS_FAILED(rv))
       return rv;
 
     // save the file.
-    
-    mLocalCachedFile = pluginTmp;
-    // Addref to add one extra refcnt, we can use NS_RELEASE2(mLocalCachedFile...) in dtor
+    CallQueryInterface(pluginTmp, &mLocalCachedFile); // no need to check return value, just addref
+    // add one extra refcnt, we can use NS_RELEASE2(mLocalCachedFile...) in dtor
     // to remove this file when refcnt == 1
     NS_ADDREF(mLocalCachedFile);
   }
 
   // add this listenerPeer to list of stream peers for this instance
   // it'll delay release of listenerPeer until nsPluginInstanceTag::~nsPluginInstanceTag
   // and the temp file is going to stay alive until then
   nsPluginInstanceTag *instanceTag = pluginHost->FindInstanceTag(mInstance);
@@ -974,73 +1169,75 @@ nsPluginStreamListenerPeer::OnStartReque
   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 = PR_TRUE;
       return NS_ERROR_FAILURE;
     }
-    mLength = 0;
+    mPluginStreamInfo->SetLength(PRUint32(0));
   }
   else {
-    mLength = length;
+    mPluginStreamInfo->SetLength(length);
   }
 
-  mRequest = request;
+  mPluginStreamInfo->SetRequest(request);
 
   nsCAutoString 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);
+  nsCAutoString urlSpec;
+  aURL->GetSpec(urlSpec);
+  mPluginStreamInfo->SetURL(urlSpec.get());
 
   if (!aContentType.IsEmpty())
-    mContentType = aContentType;
+    mPluginStreamInfo->SetContentType(aContentType.get());
 
 #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()));
+  this, request, aContentType.get(), urlSpec.get()));
 
   PR_LogFlush();
 #endif
 
   NPWindow* window = nsnull;
 
   // if we don't have an nsIPluginInstance (mInstance), it means
   // we weren't able to load a plugin previously because we
   // didn't have the mimetype.  Now that we do (aContentType),
   // we'll try again with SetUpPluginInstance()
   // which is called by InstantiateEmbeddedPlugin()
   // NOTE: we don't want to try again if we didn't get the MIME type this time
 
   if (!mInstance && mOwner && !aContentType.IsEmpty()) {
-    mOwner->GetInstance(getter_AddRefs(mInstance));
-
+    mOwner->GetInstance(mInstance);
     mOwner->GetWindow(window);
     if (!mInstance && window) {
       nsRefPtr<nsPluginHost> pluginHost = dont_AddRef(nsPluginHost::GetInst());
 
       // determine if we need to try embedded again. FullPage takes a different code path
       PRInt32 mode;
       mOwner->GetMode(&mode);
       if (mode == NP_EMBED)
         rv = pluginHost->InstantiateEmbeddedPlugin(aContentType.get(), aURL, mOwner);
       else
         rv = pluginHost->SetUpPluginInstance(aContentType.get(), aURL, mOwner);
 
       if (NS_OK == rv) {
-        mOwner->GetInstance(getter_AddRefs(mInstance));
+        // GetInstance() adds a ref
+        mOwner->GetInstance(mInstance);
         if (mInstance) {
           mInstance->Start();
           mOwner->CreateWidget();
           // If we've got a native window, the let the plugin know about it.
           if (window->window) {
             nsCOMPtr<nsIPluginInstance> inst = mInstance;
             ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
           }
@@ -1111,23 +1308,23 @@ NS_IMETHODIMP nsPluginStreamListenerPeer
         // this is not one of our range requests
         mAbort = PR_FALSE;
         return NS_BINDING_ABORTED;
       }
   }
 
   nsresult rv = NS_OK;
 
-  if (!mPStreamListener)
+  if (!mPStreamListener || !mPluginStreamInfo)
     return NS_ERROR_FAILURE;
 
-  mRequest = request;
+  mPluginStreamInfo->SetRequest(request);
 
   const char * url = nsnull;
-  GetURL(&url);
+  mPluginStreamInfo->GetURL(&url);
 
   PLUGIN_LOG(PLUGIN_LOG_NOISY,
   ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%d, length=%d, url=%s\n",
   this, request, sourceOffset, aLength, url ? url : "no url set"));
 
   // if the plugin has requested an AsFileOnly stream, then don't
   // call OnDataAvailable
   if (mStreamType != NP_ASFILEONLY) {
@@ -1150,32 +1347,32 @@ NS_IMETHODIMP nsPluginStreamListenerPeer
       //
       // Why couldn't this be tracked on the plugin info, and not in a
       // *hash table*?
       nsPRUintKey key(absoluteOffset);
       PRInt32 amtForwardToPlugin =
         NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
       mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
 
-      SetStreamOffset(absoluteOffset + amtForwardToPlugin);
+      mPluginStreamInfo->SetStreamOffset(absoluteOffset + amtForwardToPlugin);
     }
 
     nsCOMPtr<nsIInputStream> stream = aIStream;
 
     // if we are caching the file ourselves to disk, we want to 'tee' off
     // the data as the plugin read from the stream.  We do this by the magic
     // of an input stream tee.
 
     if (mFileCacheOutputStream) {
         rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
         if (NS_FAILED(rv))
             return rv;
     }
 
-    rv =  mPStreamListener->OnDataAvailable(this,
+    rv =  mPStreamListener->OnDataAvailable(mPluginStreamInfo,
                                             stream,
                                             aLength);
 
     // if a plugin returns an error, the peer must kill the stream
     //   else the stream and PluginStreamListener leak
     if (NS_FAILED(rv))
       request->Cancel(rv);
   }
@@ -1254,32 +1451,32 @@ NS_IMETHODIMP nsPluginStreamListenerPeer
     return NS_ERROR_FAILURE;
   // Set the content type to ensure we don't pass null to the plugin
   nsCAutoString aContentType;
   rv = channel->GetContentType(aContentType);
   if (NS_FAILED(rv) && !mRequestFailed)
     return rv;
 
   if (!aContentType.IsEmpty())
-    mContentType = aContentType;
+    mPluginStreamInfo->SetContentType(aContentType.get());
 
   // set error status if stream failed so we notify the plugin
   if (mRequestFailed)
     aStatus = NS_ERROR_FAILURE;
 
   if (NS_FAILED(aStatus)) {
     // on error status cleanup the stream
     // and return w/o OnFileAvailable()
-    mPStreamListener->OnStopBinding(this, aStatus);
+    mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
     return NS_OK;
   }
 
   // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
   if (mStreamType >= NP_ASFILE) {
-    nsCOMPtr<nsIFile> localFile = mLocalCachedFile;
+    nsCOMPtr<nsIFile> localFile = do_QueryInterface(mLocalCachedFile);
     if (!localFile) {
       nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
       if (cacheChannel) {
         cacheChannel->GetCacheFile(getter_AddRefs(localFile));
       } else {
         // see if it is a file channel.
         nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
         if (fileChannel) {
@@ -1290,43 +1487,41 @@ NS_IMETHODIMP nsPluginStreamListenerPeer
 
     if (localFile) {
       OnFileAvailable(localFile);
     }
   }
 
   if (mStartBinding) {
     // On start binding has been called
-    mPStreamListener->OnStopBinding(this, aStatus);
+    mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
   } else {
     // OnStartBinding hasn't been called, so complete the action.
-    mPStreamListener->OnStartBinding(this);
-    mPStreamListener->OnStopBinding(this, aStatus);
+    mPStreamListener->OnStartBinding(mPluginStreamInfo);
+    mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
   }
 
-  if (NS_SUCCEEDED(aStatus)) {
-    mStreamComplete = PR_TRUE;
-  }
-  mRequest = NULL;
+  if (NS_SUCCEEDED(aStatus))
+    mPluginStreamInfo->SetStreamComplete(PR_TRUE);
 
   return NS_OK;
 }
 
 nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
                                                          nsIURI* aURL)
 {
   nsresult rv = NS_OK;
 
   // If we don't yet have a stream listener, we need to get
   // one from the plugin.
   // NOTE: this should only happen when a stream was NOT created
   // with GetURL or PostURL (i.e. it's the initial stream we
   // send to the plugin as determined by the SRC or DATA attribute)
   if (!mPStreamListener && mInstance)
-    rv = mInstance->NewStreamToPlugin(getter_AddRefs(mPStreamListener));
+    rv = mInstance->NewStreamToPlugin(&mPStreamListener);
 
   if (NS_FAILED(rv))
     return rv;
 
   if (!mPStreamListener)
     return NS_ERROR_NULL_POINTER;
 
   PRBool useLocalCache = PR_FALSE;
@@ -1374,58 +1569,61 @@ nsresult nsPluginStreamListenerPeer::Set
                                statusText.get());
         listener->StatusLine(status.get());
       }
     }
 
     // Also provide all HTTP response headers to our listener.
     httpChannel->VisitResponseHeaders(this);
 
-    mSeekable = PR_FALSE;
+    PRBool bSeekable = PR_FALSE;
     // first we look for a content-encoding header. If we find one, we tell the
     // plugin that stream is not seekable, because the plugin always sees
     // uncompressed data, so it can't make meaningful range requests on a
     // compressed entity.  Also, we force the plugin to use
     // nsPluginStreamType_AsFile stream type and we have to save decompressed
     // file into local plugin cache, because necko cache contains original
     // compressed file.
     nsCAutoString contentEncoding;
     if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
                                                     contentEncoding))) {
       useLocalCache = PR_TRUE;
     } else {
       // set seekability (seekable if the stream has a known length and if the
       // http server accepts byte ranges).
       PRUint32 length;
-      GetLength(&length);
+      mPluginStreamInfo->GetLength(&length);
       if (length) {
         nsCAutoString range;
         if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
           range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) {
-          mSeekable = PR_TRUE;
+          bSeekable = PR_TRUE;
+          // nsPluginStreamInfo.mSeekable intitialized by PR_FALSE in ctor of nsPluginStreamInfo
+          // so we reset it only here.
+          mPluginStreamInfo->SetSeekable(bSeekable);
         }
       }
     }
 
     // we require a content len
     // get Last-Modified header for plugin info
     nsCAutoString lastModified;
     if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) &&
         !lastModified.IsEmpty()) {
       PRTime time64;
       PR_ParseTimeString(lastModified.get(), PR_TRUE, &time64);  //convert string time to integer time
 
       // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
       double fpTime;
       LL_L2D(fpTime, time64);
-      mModified = (PRUint32)(fpTime * 1e-6 + 0.5);
+      mPluginStreamInfo->SetLastModified((PRUint32)(fpTime * 1e-6 + 0.5));
     }
   }
 
-  rv = mPStreamListener->OnStartBinding(this);
+  rv = mPStreamListener->OnStartBinding(mPluginStreamInfo);
 
   mStartBinding = PR_TRUE;
 
   if (NS_FAILED(rv))
     return rv;
 
   mPStreamListener->GetStreamType(&mStreamType);
 
@@ -1459,17 +1657,17 @@ nsPluginStreamListenerPeer::OnFileAvaila
   rv = aFile->GetNativePath(path);
   if (NS_FAILED(rv)) return rv;
 
   if (path.IsEmpty()) {
     NS_WARNING("empty path");
     return NS_OK;
   }
 
-  rv = mPStreamListener->OnFileAvailable(this, path.get());
+  rv = mPStreamListener->OnFileAvailable(mPluginStreamInfo, path.get());
   return rv;
 }
 
 NS_IMETHODIMP
 nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
 {
   nsCOMPtr<nsIHTTPHeaderListener> listener = do_QueryInterface(mPStreamListener);
   if (!listener)
@@ -5168,19 +5366,19 @@ nsresult nsPluginStreamListenerPeer::Ser
     }
 #endif
     if (window->window) {
       nsCOMPtr<nsIPluginInstance> inst = mInstance;
       ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
     }
   }
 
-  mSeekable = PR_FALSE;
-  mPStreamListener->OnStartBinding(this);
-  mStreamOffset = 0;
+  mPluginStreamInfo->SetSeekable(0);
+  mPStreamListener->OnStartBinding(mPluginStreamInfo);
+  mPluginStreamInfo->SetStreamOffset(0);
 
   // force the plugin to use stream as file
   mStreamType = NP_ASFILE;
 
   // then check it out if browser cache is not available
   nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
   if (!(cacheChannel && (NS_SUCCEEDED(cacheChannel->SetCacheAsFile(PR_TRUE))))) {
       nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
@@ -5306,31 +5504,42 @@ nsPluginByteRangeStreamListener::OnDataA
   nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
   if (!finalStreamListener)
     return NS_ERROR_FAILURE;
 
   return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
 }
 
 PRBool
-nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
+nsPluginStreamInfo::UseExistingPluginCacheFile(nsPluginStreamInfo* psi)
 {
 
   NS_ENSURE_ARG_POINTER(psi);
 
  if ( psi->mLength == mLength &&
       psi->mModified == mModified &&
       mStreamComplete &&
-      mURLSpec.Equals(psi->mURLSpec))
+      !PL_strcmp(psi->mURL, mURL))
   {
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
+void
+nsPluginStreamInfo::SetStreamComplete(const PRBool complete)
+{
+  mStreamComplete = complete;
+
+  if (complete) {
+    // We're done, release the request.
+    SetRequest(nsnull);
+  }
+}
+
 // Runnable that does an async destroy of a plugin.
 
 class nsPluginDestroyRunnable : public nsRunnable,
                                 public PRCList
 {
 public:
   nsPluginDestroyRunnable(nsIPluginInstance *aInstance)
     : mInstance(aInstance)
--- a/modules/plugin/test/testplugin/nptest.cpp
+++ b/modules/plugin/test/testplugin/nptest.cpp
@@ -1006,16 +1006,19 @@ NPP_Write(NPP instance, NPStream* stream
       if (range->waiting) stillwaiting = true;
       range = reinterpret_cast<TestRange*>(range->next);
     }
     if (!stillwaiting) {
       NPError err = NPN_DestroyStream(instance, stream, NPRES_DONE);
       if (err != NPERR_NO_ERROR) {
         instanceData->err << "Error: NPN_DestroyStream returned " << err;
       }
+      if (instanceData->frame.length() > 0) {
+        sendBufferToFrame(instance);
+      }
     }
   }
   else {
     if (instanceData->streamBufSize == 0) {
       instanceData->streamBuf = malloc(len + 1);
       streamBuf = reinterpret_cast<char *>(instanceData->streamBuf);
     }
     else {