Bug 907162 - Fix MediaDecoderStateMachine might dispatch MediaDecoder::PlaybackEnded more than once and trigger multiple 'ended' events in HTMLMediaElement. r=cpearce, a=sledru
authorJW Wang <jwwang@mozilla.com>
Mon, 24 Mar 2014 11:41:46 -0400
changeset 202364 e61e0fdd1822c4d7fff97e88afc7cdeb6ace34e0
parent 202363 ca84c1c8ce16234857020126d62bd5bc5a22912b
child 202365 c8ec472bab0d617fabb45d98a690db6fd6a79cbe
push id7
push userryanvm@gmail.com
push dateTue, 22 Jul 2014 16:29:37 +0000
treeherdermozilla-b2g32_v2_0@f6eced22c26c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, sledru
bugs907162
milestone29.0
Bug 907162 - Fix MediaDecoderStateMachine might dispatch MediaDecoder::PlaybackEnded more than once and trigger multiple 'ended' events in HTMLMediaElement. r=cpearce, a=sledru
content/media/MediaDecoderStateMachine.cpp
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2381,19 +2381,26 @@ nsresult MediaDecoderStateMachine::RunSt
       // When we're decoding to a stream, the stream's main-thread finish signal
       // will take care of calling MediaDecoder::PlaybackEnded.
       if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
           !mDecoder->GetDecodedStream()) {
         int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0;
         int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock()));
         UpdatePlaybackPosition(clockTime);
 
-        nsCOMPtr<nsIRunnable> event =
-          NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
-        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+        {
+          // Wait for the state change is completed in the main thread,
+          // otherwise we might see |mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING|
+          // in next loop and send |MediaDecoder::PlaybackEnded| again to trigger 'ended'
+          // event twice in the media element.
+          ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
+          nsCOMPtr<nsIRunnable> event =
+            NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
+          NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+        }
       }
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
@@ -2875,16 +2882,22 @@ nsresult MediaDecoderStateMachine::Sched
       return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL);
     }
     // We're not currently running this state machine on the state machine
     // thread, but something has already dispatched an event to run it again,
     // so just exit; it's going to run real soon.
     return NS_OK;
   }
 
+  // Since there is already a pending task that will run immediately,
+  // we don't need to schedule a timer task.
+  if (mRunAgain) {
+    return NS_OK;
+  }
+
   mTimeout = timeout;
 
   nsresult res;
   if (!mTimer) {
     mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
     if (NS_FAILED(res)) return res;
     mTimer->SetTarget(GetStateMachineThread());
   }