Bug 1427699. P2 - rewrite InitAsCloneInternal() to mimic the case where data is downloaded from a channel. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 05 Jan 2018 16:31:39 +0800
changeset 717038 ce4e4baea6d769c5fb1d1b6f00324026e476ed14
parent 717035 146d48a6ad7d7798c26ff0ba09b1b255466aeebf
child 717058 98ba1a02b223821b39b9b0f331901cc3dce10c22
child 717148 eb8e018202fb24752f64737fcd53db98fd1dfc51
push id94538
push userjwwang@mozilla.com
push dateMon, 08 Jan 2018 03:02:37 +0000
bugs1427699
milestone59.0a1
Bug 1427699. P2 - rewrite InitAsCloneInternal() to mimic the case where data is downloaded from a channel. Note we add mClient->CacheClientSuspend() so the network state of the element is changed to IDLE because we have no channel to fetch data initially. MozReview-Commit-ID: DgJbMxvJBzH
dom/media/ChannelMediaResource.cpp
dom/media/ChannelMediaResource.h
dom/media/MediaCache.cpp
--- a/dom/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -25,17 +25,17 @@ mozilla::LazyLogModule gMediaResourceLog
 namespace mozilla {
 
 ChannelMediaResource::ChannelMediaResource(MediaResourceCallback* aCallback,
                                            nsIChannel* aChannel,
                                            nsIURI* aURI,
                                            bool aIsPrivateBrowsing)
   : BaseMediaResource(aCallback, aChannel, aURI)
   , mCacheStream(this, aIsPrivateBrowsing)
-  , mSuspendAgent(mCacheStream, !aChannel /*aSuspended*/)
+  , mSuspendAgent(mCacheStream)
 {
 }
 
 ChannelMediaResource::~ChannelMediaResource()
 {
   MOZ_ASSERT(mClosed);
   MOZ_ASSERT(!mChannel);
   MOZ_ASSERT(!mListener);
--- a/dom/media/ChannelMediaResource.h
+++ b/dom/media/ChannelMediaResource.h
@@ -18,19 +18,18 @@ namespace mozilla {
 
 /**
  * This class is responsible for managing the suspend count and report suspend
  * status of channel.
  **/
 class ChannelSuspendAgent
 {
 public:
-  ChannelSuspendAgent(MediaCacheStream& aCacheStream, bool aSuspended)
+  explicit ChannelSuspendAgent(MediaCacheStream& aCacheStream)
     : mCacheStream(aCacheStream)
-    , mSuspendCount(aSuspended ? 1 : 0)
   {
   }
 
   // True when the channel has been suspended or needs to be suspended.
   bool IsSuspended();
 
   // Return true when the channel is logically suspended, i.e. the suspend
   // count goes from 0 to 1.
@@ -45,17 +44,17 @@ public:
   void Revoke();
 
 private:
   // Only suspends channel but not changes the suspend count.
   void SuspendInternal();
 
   nsIChannel* mChannel = nullptr;
   MediaCacheStream& mCacheStream;
-  uint32_t mSuspendCount;
+  uint32_t mSuspendCount = 0;
   bool mIsChannelSuspended = false;
 };
 
 DDLoggedTypeDeclNameAndBase(ChannelMediaResource, BaseMediaResource);
 
 /**
  * This is the MediaResource implementation that wraps Necko channels.
  * Much of its functionality is actually delegated to MediaCache via
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -2869,57 +2869,42 @@ MediaCacheStream::Init(int64_t aContentL
 void
 MediaCacheStream::InitAsClone(MediaCacheStream* aOriginal)
 {
   MOZ_ASSERT(!mMediaCache, "Has been initialized.");
   MOZ_ASSERT(aOriginal->mMediaCache, "Don't clone an uninitialized stream.");
 
   // Use the same MediaCache as our clone.
   mMediaCache = aOriginal->mMediaCache;
-  // This needs to be done before OpenStream() to avoid data race.
-  mClientSuspended = true;
-  // Cloned streams are initially suspended, since there is no channel open
-  // initially for a clone.
-  mCacheSuspended = true;
-  mChannelEnded = true;
-
   OwnerThread()->Dispatch(
     NS_NewRunnableFunction("MediaCacheStream::InitAsClone", [
       this,
       aOriginal,
       r1 = RefPtr<ChannelMediaResource>(mClient),
       r2 = RefPtr<ChannelMediaResource>(aOriginal->mClient)
     ]() { InitAsCloneInternal(aOriginal); }));
 }
 
 void
 MediaCacheStream::InitAsCloneInternal(MediaCacheStream* aOriginal)
 {
   MOZ_ASSERT(OwnerThread()->IsOnCurrentThread());
-  AutoLock lock(aOriginal->mMediaCache->Monitor());
+  AutoLock lock(mMediaCache->Monitor());
 
+  // Download data and notify events if necessary. Note the order is important
+  // in order to mimic the behavior of data being downloaded from the channel.
+
+  // Step 1: copy/download data from the original stream.
   mResourceID = aOriginal->mResourceID;
-
-  // Grab cache blocks from aOriginal as readahead blocks for our stream
   mStreamLength = aOriginal->mStreamLength;
   mIsTransportSeekable = aOriginal->mIsTransportSeekable;
   mDownloadStatistics = aOriginal->mDownloadStatistics;
   mDownloadStatistics.Stop();
 
-  // Notify the client that we have new data so the decoder has a chance to
-  // compute 'canplaythrough' and buffer ranges.
-  mClient->CacheClientNotifyDataReceived();
-
-  if (aOriginal->mDidNotifyDataEnded &&
-      NS_SUCCEEDED(aOriginal->mNotifyDataEndedStatus)) {
-    mNotifyDataEndedStatus = aOriginal->mNotifyDataEndedStatus;
-    mDidNotifyDataEnded = true;
-    mClient->CacheClientNotifyDataEnded(mNotifyDataEndedStatus);
-  }
-
+  // Grab cache blocks from aOriginal as readahead blocks for our stream
   for (uint32_t i = 0; i < aOriginal->mBlocks.Length(); ++i) {
     int32_t cacheBlockIndex = aOriginal->mBlocks[i];
     if (cacheBlockIndex < 0)
       continue;
 
     while (i >= mBlocks.Length()) {
       mBlocks.AppendElement(-1);
     }
@@ -2929,18 +2914,36 @@ MediaCacheStream::InitAsCloneInternal(Me
   }
 
   // Copy the partial block.
   mChannelOffset = aOriginal->mChannelOffset;
   memcpy(mPartialBlockBuffer.get(),
          aOriginal->mPartialBlockBuffer.get(),
          BLOCK_SIZE);
 
+  // Step 2: notify the client that we have new data so the decoder has a chance
+  // to compute 'canplaythrough' and buffer ranges.
+  mClient->CacheClientNotifyDataReceived();
+
+  // Step 3: notify download ended if necessary.
+  if (aOriginal->mDidNotifyDataEnded &&
+      NS_SUCCEEDED(aOriginal->mNotifyDataEndedStatus)) {
+    mNotifyDataEndedStatus = aOriginal->mNotifyDataEndedStatus;
+    mDidNotifyDataEnded = true;
+    mClient->CacheClientNotifyDataEnded(mNotifyDataEndedStatus);
+  }
+
+  // Step 4: notify download is suspended by the cache.
+  mClientSuspended = true;
+  mCacheSuspended = true;
+  mChannelEnded = true;
+  mClient->CacheClientSuspend();
+
+  // Step 5: add the stream to be managed by the cache.
   mMediaCache->OpenStream(lock, this, true /* aIsClone */);
-
   // Wake up the reader which is waiting for the cloned data.
   lock.NotifyAll();
 }
 
 nsIEventTarget*
 MediaCacheStream::OwnerThread() const
 {
   return mMediaCache->OwnerThread();