Bug 1151804 - Ensure that streams are not prematurely terminated when asyncInit is true. r=lizzard
☠☠ backed out by a1d5331b1823 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Mon, 13 Apr 2015 13:19:51 -0600
changeset 265649 7c2d250ea3f251575544176861959ff3aa7fa26a
parent 265648 662ee7a4e9471e4849f6713f78a97e2084994a05
child 265650 fb10a430555d03019ce9dd9c203183e9f18080d0
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslizzard
bugs1151804
milestone39.0a2
Bug 1151804 - Ensure that streams are not prematurely terminated when asyncInit is true. r=lizzard
dom/plugins/base/nsNPAPIPluginStreamListener.cpp
dom/plugins/base/nsNPAPIPluginStreamListener.h
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -144,16 +144,18 @@ nsNPAPIPluginStreamListener::nsNPAPIPlug
   , mStreamCleanedUp(false)
   , mCallNotify(notifyData ? true : false)
   , mIsSuspended(false)
   , mIsPluginInitJSStream(mInst->mInPluginInitCall &&
                           aURL && strncmp(aURL, "javascript:",
                                           sizeof("javascript:") - 1) == 0)
   , mRedirectDenied(false)
   , mResponseHeaderBuf(nullptr)
+  , mStreamStopMode(eNormalStop)
+  , mPendingStopBindingStatus(NS_OK)
 {
   mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this);
   mNPStreamWrapper->mNPStream.notifyData = notifyData;
 }
 
 nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener()
 {
   // remove this from the plugin instance's stream list
@@ -329,19 +331,16 @@ nsNPAPIPluginStreamListener::OnStartBind
                  ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
                   this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url));
   
   if (error != NPERR_NO_ERROR)
     return NS_ERROR_FAILURE;
 
   mStreamState = eNewStreamCalled;
 
-  if (streamType == nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN) {
-    SuspendRequest();
-  }
   if (!SetStreamType(streamType, false)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 bool
@@ -365,18 +364,19 @@ nsNPAPIPluginStreamListener::SetStreamTy
       // 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;
     case nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN:
       MOZ_ASSERT(!aNeedsResume);
       mStreamType = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
-      // In this case we just want to set mStreamType but we do not want to
-      // execute anything else in this function.
+      SuspendRequest();
+      mStreamStopMode = eDoDeferredStop;
+      // In this case we do not want to execute anything else in this function.
       return true;
     default:
       return false;
   }
   mStreamState = eStreamTypeSet;
   if (aNeedsResume) {
     if (mStreamListenerPeer) {
       mStreamListenerPeer->OnStreamTypeSet(mStreamType);
@@ -761,28 +761,41 @@ nsNPAPIPluginStreamListener::OnFileAvail
   
   return NS_OK;
 }
 
 nsresult
 nsNPAPIPluginStreamListener::OnStopBinding(nsPluginStreamListenerPeer* streamPeer, 
                                            nsresult status)
 {
-  StopDataPump();
-  
   if (NS_FAILED(status)) {
     // The stream was destroyed, or died for some reason. Make sure we
     // cancel the underlying request.
     if (mStreamListenerPeer) {
       mStreamListenerPeer->CancelRequests(status);
     }
   }
-  
-  if (!mInst || !mInst->CanFireNotifications())
+
+  if (!mInst || !mInst->CanFireNotifications()) {
+    StopDataPump();
     return NS_ERROR_FAILURE;
+  }
+
+  // We need to detect that the stop is due to async stream init completion.
+  if (mStreamStopMode == eDoDeferredStop) {
+    // We shouldn't be delivering this until async init is done
+    mStreamStopMode = eStopPending;
+    mPendingStopBindingStatus = status;
+    if (!mDataPumpTimer) {
+      StartDataPump();
+    }
+    return NS_OK;
+  }
+
+  StopDataPump();
 
   NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
   if (mRedirectDenied || status == NS_BINDING_ABORTED) {
     reason = NPRES_USER_BREAK;
   }
 
   // The following code can result in the deletion of 'this'. Don't
   // assume we are alive after this!
@@ -802,43 +815,56 @@ nsNPAPIPluginStreamListener::OnStopBindi
 
 nsresult
 nsNPAPIPluginStreamListener::GetStreamType(int32_t *result)
 {
   *result = mStreamType;
   return NS_OK;
 }
 
+bool
+nsNPAPIPluginStreamListener::MaybeRunStopBinding()
+{
+  if (mIsSuspended || mStreamStopMode != eStopPending) {
+    return false;
+  }
+  OnStopBinding(mStreamListenerPeer, mPendingStopBindingStatus);
+  mStreamStopMode = eNormalStop;
+  return true;
+}
+
 NS_IMETHODIMP
 nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer)
 {
   NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?");
   
   int32_t oldStreamBufferByteCount = mStreamBufferByteCount;
   
   nsresult rv = OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount);
   
   if (NS_FAILED(rv)) {
     // We ran into an error, no need to keep firing this timer then.
-    aTimer->Cancel();
+    StopDataPump();
+    MaybeRunStopBinding();
     return NS_OK;
   }
   
   if (mStreamBufferByteCount != oldStreamBufferByteCount &&
       ((mStreamState == eStreamTypeSet && mStreamBufferByteCount < 1024) ||
        mStreamBufferByteCount == 0)) {
         // The plugin read some data and we've got less than 1024 bytes in
         // our buffer (or its empty and the stream is already
         // done). Resume the request so that we get more data off the
         // network.
         ResumeRequest();
         // Necko will pump data now that we've resumed the request.
         StopDataPump();
       }
-  
+
+  MaybeRunStopBinding();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginStreamListener::StatusLine(const char* line)
 {
   mResponseHeaders.Append(line);
   mResponseHeaders.Append('\n');
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.h
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h
@@ -108,17 +108,26 @@ protected:
 
   enum StreamState
   {
     eStreamStopped = 0, // The stream is stopped
     eNewStreamCalled,   // NPP_NewStream was called but has not completed yet
     eStreamTypeSet      // The stream is fully initialized
   };
 
+  enum StreamStopMode
+  {
+    eNormalStop = 0,
+    eDoDeferredStop,
+    eStopPending
+  };
+
   virtual ~nsNPAPIPluginStreamListener();
+  bool MaybeRunStopBinding();
+
   char* mStreamBuffer;
   char* mNotifyURL;
   nsRefPtr<nsNPAPIPluginInstance> mInst;
   nsNPAPIStreamWrapper *mNPStreamWrapper;
   uint32_t mStreamBufferSize;
   int32_t mStreamBufferByteCount;
   int32_t mStreamType;
   StreamState mStreamState;
@@ -126,14 +135,16 @@ protected:
   bool mCallNotify;
   bool mIsSuspended;
   bool mIsPluginInitJSStream;
   bool mRedirectDenied;
   nsCString mResponseHeaders;
   char* mResponseHeaderBuf;
   nsCOMPtr<nsITimer> mDataPumpTimer;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mHTTPRedirectCallback;
+  StreamStopMode mStreamStopMode;
+  nsresult mPendingStopBindingStatus;
 
 public:
   nsRefPtr<nsPluginStreamListenerPeer> mStreamListenerPeer;
 };
 
 #endif // nsNPAPIPluginStreamListener_h_