Bug 1309116. Part 2 - rewrite StateObject::SetState using variadic template. draft
authorJW Wang <jwwang@mozilla.com>
Tue, 11 Oct 2016 14:41:07 +0800
changeset 425044 ea4a14f8e64228e492000611c0e4e70c44792369
parent 425043 6ecdcd48b954df49d142702605c2c9dd54478d8b
child 425045 5f32414bc1448b8dbb6586141117b65548118cdf
push id32322
push userjwwang@mozilla.com
push dateFri, 14 Oct 2016 02:59:40 +0000
bugs1309116
milestone52.0a1
Bug 1309116. Part 2 - rewrite StateObject::SetState using variadic template. MozReview-Commit-ID: 3ofT2po9B4Q
dom/media/MediaDecoderStateMachine.cpp
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -221,17 +221,35 @@ protected:
   explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
   TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
   MediaResource* Resource() const { return mMaster->mResource; }
   MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
   const MediaInfo& Info() const { return mMaster->Info(); }
 
   // Note this function will delete the current state object.
   // Don't access members to avoid UAF after this call.
-  void SetState(State aState) { mMaster->SetState(aState); }
+  template <class S, typename... Ts>
+  void SetState(Ts&&... aArgs)
+  {
+    // keep mMaster in a local object because mMaster will become invalid after
+    // the current state object is deleted.
+    auto master = mMaster;
+
+    UniquePtr<StateObject> s = MakeUnique<S>(master, Forward<Ts>(aArgs)...);
+    if (master->mState == s->GetState()) {
+      return;
+    }
+
+    SLOG("change state to: %s", ToStateStr(s->GetState()));
+
+    Exit();
+    master->mState = s->GetState();
+    master->mStateObj = Move(s); // Will delete |this|!
+    master->mStateObj->Enter();
+  }
 
   // Take a raw pointer in order not to change the life cycle of MDSM.
   // It is guaranteed to be valid by MDSM.
   Master* mMaster;
 };
 
 class MediaDecoderStateMachine::DecodeMetadataState
   : public MediaDecoderStateMachine::StateObject
@@ -873,17 +891,17 @@ StateObject::HandleDormant(bool aDormant
   mMaster->mQueuedSeek.mTarget =
     SeekTarget(mMaster->mCurrentPosition,
                SeekTarget::Accurate,
                MediaDecoderEventVisibility::Suppressed);
   // SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
   // need to create the promise even it is not used at all.
   RefPtr<MediaDecoder::SeekPromise> unused =
     mMaster->mQueuedSeek.mPromise.Ensure(__func__);
-  SetState(DECODER_STATE_DORMANT);
+  SetState<DormantState>();
   return true;
 }
 
 void
 MediaDecoderStateMachine::
 DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
 {
   mMetadataRequest.Complete();
@@ -931,59 +949,61 @@ DecodeMetadataState::OnMetadataRead(Meta
     mMaster->mDuration.Ref().isSome() || waitingForCDM;
 
   if (mMaster->mNotifyMetadataBeforeFirstFrame) {
     mMaster->EnqueueLoadedMetadataEvent();
   }
 
   if (mPendingDormant) {
     // No need to store mQueuedSeek because we are at position 0.
-    SetState(DECODER_STATE_DORMANT);
+    SetState<DormantState>();
     return;
   }
 
   if (waitingForCDM) {
     // Metadata parsing was successful but we're still waiting for CDM caps
     // to become available so that we can build the correct decryptor/decoder.
-    SetState(DECODER_STATE_WAIT_FOR_CDM);
+    SetState<WaitForCDMState>();
     return;
   }
 
-  SetState(DECODER_STATE_DECODING_FIRSTFRAME);
+  SetState<DecodingFirstFrameState>();
 }
 
 bool
 MediaDecoderStateMachine::
 WaitForCDMState::HandleDormant(bool aDormant)
 {
   if (aDormant) {
     // No need to store mQueuedSeek because we are at position 0.
-    SetState(DECODER_STATE_DORMANT);
+    SetState<DormantState>();
   }
   return true;
 }
 
 bool
 MediaDecoderStateMachine::
 DormantState::HandleDormant(bool aDormant)
 {
   if (!aDormant) {
     // Exit dormant state. Check if we need the CDMProxy to start decoding.
-    SetState(Info().IsEncrypted() && !mMaster->mCDMProxy
-      ? DECODER_STATE_WAIT_FOR_CDM
-      : DECODER_STATE_DECODING_FIRSTFRAME);
+    if (Info().IsEncrypted() && !mMaster->mCDMProxy) {
+      SetState<WaitForCDMState>();
+    } else {
+      SetState<DecodingFirstFrameState>();
+    }
   }
   return true;
 }
 
 bool
 MediaDecoderStateMachine::
 WaitForCDMState::HandleCDMProxyReady()
 {
-  SetState(DECODER_STATE_DECODING_FIRSTFRAME);
+  SetState<DecodingFirstFrameState>();
   return true;
 }
 
 void
 MediaDecoderStateMachine::
 DecodingFirstFrameState::Enter()
 {
   // Handle pending seek.
@@ -991,17 +1011,17 @@ DecodingFirstFrameState::Enter()
       (mMaster->mSentFirstFrameLoadedEvent ||
        Reader()->ForceZeroStartTime())) {
     mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
     return;
   }
 
   // Transition to DECODING if we've decoded first frames.
   if (mMaster->mSentFirstFrameLoadedEvent) {
-    SetState(DECODER_STATE_DECODING);
+    SetState<DecodingState>();
     return;
   }
 
   // Dispatch tasks to decode first frames.
   mMaster->DispatchDecodeTasksIfNeeded();
 }
 
 RefPtr<MediaDecoder::SeekPromise>
@@ -1042,31 +1062,31 @@ DecodingFirstFrameState::MaybeFinishDeco
     return;
   }
 
   mMaster->FinishDecodeFirstFrame();
 
   if (mMaster->mQueuedSeek.Exists()) {
     mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
   } else {
-    SetState(DECODER_STATE_DECODING);
+    SetState<DecodingState>();
   }
 }
 
 void
 MediaDecoderStateMachine::
 DecodingState::Enter()
 {
   MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
   // Pending seek should've been handled by DECODING_FIRSTFRAME before
   // transitioning to DECODING.
   MOZ_ASSERT(!mMaster->mQueuedSeek.Exists());
 
   if (mMaster->CheckIfDecodeComplete()) {
-    SetState(DECODER_STATE_COMPLETED);
+    SetState<CompletedState>();
     return;
   }
 
   mDecodeStartTime = TimeStamp::Now();
 
   MaybeStopPrerolling();
 
   // Ensure that we've got tasks enqueued to decode data if we need to.
@@ -1088,17 +1108,17 @@ DecodingState::HandleSeek(SeekTarget aTa
   return p.forget();
 }
 
 bool
 MediaDecoderStateMachine::
 DecodingState::HandleEndOfStream()
 {
   if (mMaster->CheckIfDecodeComplete()) {
-    SetState(DECODER_STATE_COMPLETED);
+    SetState<CompletedState>();
   } else {
     MaybeStopPrerolling();
   }
   return true;
 }
 
 bool
 MediaDecoderStateMachine::
@@ -1113,17 +1133,17 @@ SeekingState::HandleDormant(bool aDorman
   // method later, we treat a VideoOnly seek task as a normal Accurate
   // seek task so that while it is resumed, both audio and video playback
   // are handled.
   if (mSeekJob.mTarget.IsVideoOnly()) {
     mSeekJob.mTarget.SetType(SeekTarget::Accurate);
     mSeekJob.mTarget.SetVideoOnly(false);
   }
   mMaster->mQueuedSeek = Move(mSeekJob);
-  SetState(DECODER_STATE_DORMANT);
+  SetState<DormantState>();
   return true;
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 SeekingState::HandleSeek(SeekTarget aTarget)
 {
   mMaster->mQueuedSeek.RejectIfExists(__func__);
@@ -1203,17 +1223,21 @@ SeekingState::SeekCompleted()
   // Try to decode another frame to detect if we're at the end...
   SLOG("Seek completed, mCurrentPosition=%lld", mMaster->mCurrentPosition.Ref());
 
   if (video) {
     mMaster->mMediaSink->Redraw(Info().mVideo);
     mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
   }
 
-  SetState(nextState);
+  if (nextState == DECODER_STATE_COMPLETED) {
+    SetState<CompletedState>();
+  } else {
+    SetState<DecodingState>();
+  }
 }
 
 void
 MediaDecoderStateMachine::
 BufferingState::Step()
 {
   TimeStamp now = TimeStamp::Now();
   MOZ_ASSERT(!mBufferingStart.IsNull(), "Must know buffering start time.");
@@ -1248,25 +1272,25 @@ BufferingState::Step()
     SLOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
          "mAudioStatus: %s, outOfVideo: %d, mVideoStatus: %s",
          mMaster->OutOfDecodedAudio(), mMaster->AudioRequestStatus(),
          mMaster->OutOfDecodedVideo(), mMaster->VideoRequestStatus());
     return;
   }
 
   SLOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
-  SetState(DECODER_STATE_DECODING);
+  SetState<DecodingState>();
 }
 
 bool
 MediaDecoderStateMachine::
 BufferingState::HandleEndOfStream()
 {
   if (mMaster->CheckIfDecodeComplete()) {
-    SetState(DECODER_STATE_COMPLETED);
+    SetState<CompletedState>();
   } else {
     // Check if we can exit buffering.
     mMaster->ScheduleStateMachine();
   }
   return true;
 }
 
 RefPtr<MediaDecoder::SeekPromise>