Bug 1324335. Part 2 - Make SeekJob::mTarget a Maybe<>. draft
authorJW Wang <jwwang@mozilla.com>
Mon, 19 Dec 2016 12:25:14 +0800
changeset 451005 baadb4aaec8cae54966ea0f8c28bfdf31f0f49f1
parent 451004 72ab826e348e7cb33847efd5db6ea81fa2d11639
child 451006 b0591bdee6e699b5b7fa704ce45729ae37316f4c
push id39013
push userjwwang@mozilla.com
push dateMon, 19 Dec 2016 09:44:15 +0000
bugs1324335
milestone53.0a1
Bug 1324335. Part 2 - Make SeekJob::mTarget a Maybe<>. MozReview-Commit-ID: A2M3b131nHH
dom/media/MediaDecoderStateMachine.cpp
dom/media/SeekJob.cpp
dom/media/SeekJob.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -381,17 +381,17 @@ public:
   }
 
   void HandleCDMProxyReady() override;
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
   {
     SLOG("Not Enough Data to seek at this stage, queuing seek");
     mPendingSeek.RejectIfExists(__func__);
-    mPendingSeek.mTarget = aTarget;
+    mPendingSeek.mTarget.emplace(aTarget);
     return mPendingSeek.mPromise.Ensure(__func__);
   }
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing since no decoders are created yet.
   }
 
@@ -422,17 +422,17 @@ public:
     if (mMaster->IsPlaying()) {
       mMaster->StopPlayback();
     }
 
     // Calculate the position to seek to when exiting dormant.
     auto t = mMaster->mMediaSink->IsStarted()
       ? mMaster->GetClock()
       : mMaster->GetMediaTime();
-    mPendingSeek.mTarget = SeekTarget(t, SeekTarget::Accurate);
+    mPendingSeek.mTarget.emplace(t, SeekTarget::Accurate);
     // SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
     // need to create the promise even it is not used at all.
     RefPtr<MediaDecoder::SeekPromise> x = mPendingSeek.mPromise.Ensure(__func__);
 
     mMaster->Reset();
     mMaster->mReader->ReleaseResources();
   }
 
@@ -770,21 +770,21 @@ public:
       mMaster->mVideoDecodeSuspended = false;
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::ExitVideoSuspend);
       Reader()->SetVideoBlankDecode(false);
     }
 
     CreateSeekTask();
 
     // Don't stop playback for a video-only seek since audio is playing.
-    if (!mSeekJob.mTarget.IsVideoOnly()) {
+    if (!mSeekJob.mTarget->IsVideoOnly()) {
       mMaster->StopPlayback();
     }
 
-    mMaster->UpdatePlaybackPositionInternal(mSeekJob.mTarget.GetTime().ToMicroseconds());
+    mMaster->UpdatePlaybackPositionInternal(mSeekJob.mTarget->GetTime().ToMicroseconds());
 
     if (aVisibility == EventVisibility::Observable) {
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
       // We want dormant actions to be transparent to the user.
       // So we only notify the change when the seek request is from the user.
       mMaster->UpdateNextFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
     }
 
@@ -846,17 +846,17 @@ class MediaDecoderStateMachine::Accurate
 public:
   explicit AccurateSeekingState(Master* aPtr) : SeekingState(aPtr)
   {
   }
 
   RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
                                           EventVisibility aVisibility)
   {
-    MOZ_ASSERT(aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast());
+    MOZ_ASSERT(aSeekJob.mTarget->IsAccurate() || aSeekJob.mTarget->IsFast());
     return SeekingState::Enter(Move(aSeekJob), aVisibility);
   }
 
   void Exit() override
   {
     // Disconnect MediaDecoder.
     mSeekJob.RejectIfExists(__func__);
 
@@ -867,24 +867,24 @@ public:
   void HandleAudioDecoded(MediaData* aAudio) override
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
     MOZ_ASSERT(aAudio);
 
     // Video-only seek doesn't reset audio decoder. There might be pending audio
     // requests when AccurateSeekTask::Seek() begins. We will just store the data
     // without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
-    if (mSeekJob.mTarget.IsVideoOnly()) {
+    if (mSeekJob.mTarget->IsVideoOnly()) {
       mMaster->Push(aAudio);
       return;
     }
 
     AdjustFastSeekIfNeeded(aAudio);
 
-    if (mSeekJob.mTarget.IsFast()) {
+    if (mSeekJob.mTarget->IsFast()) {
       // Non-precise seek; we can stop the seek at the first sample.
       mMaster->Push(aAudio);
       mDoneAudioSeeking = true;
     } else {
       nsresult rv = DropAudioUpToSeekTarget(aAudio->As<AudioData>());
       if (NS_FAILED(rv)) {
         mMaster->DecodeError(rv);
         return;
@@ -900,17 +900,17 @@ public:
 
   void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
     MOZ_ASSERT(aVideo);
 
     AdjustFastSeekIfNeeded(aVideo);
 
-    if (mSeekJob.mTarget.IsFast()) {
+    if (mSeekJob.mTarget->IsFast()) {
       // Non-precise seek. We can stop the seek at the first sample.
       mMaster->Push(aVideo);
       mDoneVideoSeeking = true;
     } else {
       nsresult rv = DropVideoUpToSeekTarget(aVideo);
       if (NS_FAILED(rv)) {
         mMaster->DecodeError(rv);
         return;
@@ -924,17 +924,17 @@ public:
     MaybeFinishSeek();
   }
 
   void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
 
     // Ignore pending requests from video-only seek.
-    if (aType == MediaData::AUDIO_DATA && mSeekJob.mTarget.IsVideoOnly()) {
+    if (aType == MediaData::AUDIO_DATA && mSeekJob.mTarget->IsVideoOnly()) {
       return;
     }
 
     // If the decoder is waiting for data, we tell it to call us back when the
     // data arrives.
     if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
       Reader()->WaitForData(aType);
       return;
@@ -970,17 +970,17 @@ public:
     mMaster->DecodeError(aError);
   }
 
   void HandleAudioWaited(MediaData::Type aType) override
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
 
     // Ignore pending requests from video-only seek.
-    if (mSeekJob.mTarget.IsVideoOnly()) {
+    if (mSeekJob.mTarget->IsVideoOnly()) {
       return;
     }
     RequestAudioData();
   }
 
   void HandleVideoWaited(MediaData::Type aType) override
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
@@ -992,57 +992,57 @@ public:
   {
     MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
   }
 
 private:
   void CreateSeekTask() override
   {
     mCurrentTimeBeforeSeek = TimeUnit::FromMicroseconds(mMaster->GetMediaTime());
-    mDoneAudioSeeking = !Info().HasAudio() || mSeekJob.mTarget.IsVideoOnly();
+    mDoneAudioSeeking = !Info().HasAudio() || mSeekJob.mTarget->IsVideoOnly();
     mDoneVideoSeeking = !Info().HasVideo();
   }
 
   void ResetMDSM() override
   {
-    if (mSeekJob.mTarget.IsVideoOnly()) {
+    if (mSeekJob.mTarget->IsVideoOnly()) {
       mMaster->Reset(TrackInfo::kVideoTrack);
     } else {
       mMaster->Reset();
     }
   }
 
   void DoSeek() override
   {
     // Request the demuxer to perform seek.
-    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget, mMaster->Duration())
+    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget.ref(), mMaster->Duration())
       ->Then(OwnerThread(), __func__,
              [this] (media::TimeUnit aUnit) {
                OnSeekResolved(aUnit);
              },
              [this] (const MediaResult& aError) {
                OnSeekRejected(aError);
              }));
   }
 
   int64_t CalculateNewCurrentTime() const override
   {
-    const int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
+    const int64_t seekTime = mSeekJob.mTarget->GetTime().ToMicroseconds();
 
     // For the accurate seek, we always set the newCurrentTime = seekTime so that
     // the updated HTMLMediaElement.currentTime will always be the seek target;
     // we rely on the MediaSink to handles the gap between the newCurrentTime and
     // the real decoded samples' start time.
-    if (mSeekJob.mTarget.IsAccurate()) {
+    if (mSeekJob.mTarget->IsAccurate()) {
       return seekTime;
     }
 
     // For the fast seek, we update the newCurrentTime with the decoded audio and
     // video samples, set it to be the one which is closet to the seekTime.
-    if (mSeekJob.mTarget.IsFast()) {
+    if (mSeekJob.mTarget->IsFast()) {
       RefPtr<MediaData> audio = AudioQueue().PeekFront();
       RefPtr<MediaData> video = VideoQueue().PeekFront();
 
       // A situation that both audio and video approaches the end.
       if (!audio && !video) {
         return seekTime;
       }
 
@@ -1089,68 +1089,68 @@ private:
     MOZ_ASSERT(!mDoneVideoSeeking);
     MOZ_ASSERT(!Reader()->IsRequestingVideoData());
     MOZ_ASSERT(!Reader()->IsWaitingVideoData());
     Reader()->RequestVideoData(false, media::TimeUnit());
   }
 
   void AdjustFastSeekIfNeeded(MediaData* aSample)
   {
-    if (mSeekJob.mTarget.IsFast() &&
-        mSeekJob.mTarget.GetTime() > mCurrentTimeBeforeSeek &&
+    if (mSeekJob.mTarget->IsFast() &&
+        mSeekJob.mTarget->GetTime() > mCurrentTimeBeforeSeek &&
         aSample->mTime < mCurrentTimeBeforeSeek.ToMicroseconds()) {
       // We are doing a fastSeek, but we ended up *before* the previous
       // playback position. This is surprising UX, so switch to an accurate
       // seek and decode to the seek target. This is not conformant to the
       // spec, fastSeek should always be fast, but until we get the time to
       // change all Readers to seek to the keyframe after the currentTime
       // in this case, we'll just decode forward. Bug 1026330.
-      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
+      mSeekJob.mTarget->SetType(SeekTarget::Accurate);
     }
   }
 
   nsresult DropAudioUpToSeekTarget(AudioData* aAudio)
   {
-    MOZ_ASSERT(aAudio && mSeekJob.mTarget.IsAccurate());
+    MOZ_ASSERT(aAudio && mSeekJob.mTarget->IsAccurate());
 
     CheckedInt64 sampleDuration = FramesToUsecs(aAudio->mFrames, Info().mAudio.mRate);
     if (!sampleDuration.isValid()) {
       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     }
 
-    if (aAudio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+    if (aAudio->mTime + sampleDuration.value() <= mSeekJob.mTarget->GetTime().ToMicroseconds()) {
       // Our seek target lies after the frames in this AudioData. Don't
       // push it onto the audio queue, and keep decoding forwards.
       return NS_OK;
     }
 
-    if (aAudio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+    if (aAudio->mTime > mSeekJob.mTarget->GetTime().ToMicroseconds()) {
       // The seek target doesn't lie in the audio block just after the last
       // audio frames we've seen which were before the seek target. This
       // could have been the first audio data we've seen after seek, i.e. the
       // seek terminated after the seek target in the audio stream. Just
       // abort the audio decode-to-target, the state machine will play
       // silence to cover the gap. Typically this happens in poorly muxed
       // files.
       SWARN("Audio not synced after seek, maybe a poorly muxed file?");
       mMaster->Push(aAudio);
       mDoneAudioSeeking = true;
       return NS_OK;
     }
 
     // The seek target lies somewhere in this AudioData's frames, strip off
     // any frames which lie before the seek target, so we'll begin playback
     // exactly at the seek target.
-    NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= aAudio->mTime,
+    NS_ASSERTION(mSeekJob.mTarget->GetTime().ToMicroseconds() >= aAudio->mTime,
                  "Target must at or be after data start.");
-    NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < aAudio->mTime + sampleDuration.value(),
+    NS_ASSERTION(mSeekJob.mTarget->GetTime().ToMicroseconds() < aAudio->mTime + sampleDuration.value(),
                  "Data must end after target.");
 
     CheckedInt64 framesToPrune =
-      UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - aAudio->mTime, Info().mAudio.mRate);
+      UsecsToFrames(mSeekJob.mTarget->GetTime().ToMicroseconds() - aAudio->mTime, Info().mAudio.mRate);
     if (!framesToPrune.isValid()) {
       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     }
     if (framesToPrune.value() > aAudio->mFrames) {
       // We've messed up somehow. Don't try to trim frames, the |frames|
       // variable below will overflow.
       SWARN("Can't prune more frames that we have!");
       return NS_ERROR_FAILURE;
@@ -1165,17 +1165,17 @@ private:
     memcpy(audioData.get(),
            aAudio->mAudioData.get() + (framesToPrune.value() * channels),
            frames * channels * sizeof(AudioDataValue));
     CheckedInt64 duration = FramesToUsecs(frames, Info().mAudio.mRate);
     if (!duration.isValid()) {
       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     }
     RefPtr<AudioData> data(new AudioData(aAudio->mOffset,
-                                         mSeekJob.mTarget.GetTime().ToMicroseconds(),
+                                         mSeekJob.mTarget->GetTime().ToMicroseconds(),
                                          duration.value(),
                                          frames,
                                          Move(audioData),
                                          channels,
                                          aAudio->mRate));
     MOZ_ASSERT(AudioQueue().GetSize() == 0, "Should be the 1st sample after seeking");
     mMaster->Push(data);
     mDoneAudioSeeking = true;
@@ -1184,17 +1184,17 @@ private:
   }
 
   nsresult DropVideoUpToSeekTarget(MediaData* aSample)
   {
     RefPtr<VideoData> video(aSample->As<VideoData>());
     MOZ_ASSERT(video);
     SLOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
          video->mTime, video->GetEndTime());
-    const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
+    const int64_t target = mSeekJob.mTarget->GetTime().ToMicroseconds();
 
     // If the frame end time is less than the seek target, we won't want
     // to display this frame after the seek, so discard it.
     if (target >= video->GetEndTime()) {
       SLOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
            video->mTime, video->GetEndTime(), target);
       mFirstVideoFrameAfterSeek = video;
     } else {
@@ -1265,17 +1265,17 @@ class MediaDecoderStateMachine::NextFram
 public:
   explicit NextFrameSeekingState(Master* aPtr) : SeekingState(aPtr)
   {
   }
 
   RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
                                           EventVisibility aVisibility)
   {
-    MOZ_ASSERT(aSeekJob.mTarget.IsNextFrame());
+    MOZ_ASSERT(aSeekJob.mTarget->IsNextFrame());
     return SeekingState::Enter(Move(aSeekJob), aVisibility);
   }
 
   void Exit() override
   {
     // Disconnect my async seek operation.
     mAsyncSeekTask->Cancel();
 
@@ -1490,17 +1490,17 @@ private:
       MOZ_ASSERT_UNREACHABLE("We cannot handle RAW_DATA or NULL_DATA here.");
     }
   }
 
   int64_t CalculateNewCurrentTime() const override
   {
     // The HTMLMediaElement.currentTime should be updated to the seek target
     // which has been updated to the next frame's time.
-    return mSeekJob.mTarget.GetTime().ToMicroseconds();
+    return mSeekJob.mTarget->GetTime().ToMicroseconds();
   }
 
   void OnSeekTaskResolved()
   {
     if (mSeekedAudioData) {
       mMaster->Push(mSeekedAudioData);
       mMaster->mDecodedAudioEndTime = std::max(
         mSeekedAudioData->GetEndTime(), mMaster->mDecodedAudioEndTime);
@@ -1570,32 +1570,32 @@ private:
   }
 
   // Update the seek target's time before resolving this seek task, the updated
   // time will be used in the MDSM::SeekCompleted() to update the MDSM's position.
   void UpdateSeekTargetTime()
   {
     RefPtr<MediaData> data = VideoQueue().PeekFront();
     if (data) {
-      mSeekJob.mTarget.SetTime(TimeUnit::FromMicroseconds(data->mTime));
+      mSeekJob.mTarget->SetTime(TimeUnit::FromMicroseconds(data->mTime));
     } else if (mSeekedVideoData) {
-      mSeekJob.mTarget.SetTime(TimeUnit::FromMicroseconds(mSeekedVideoData->mTime));
+      mSeekJob.mTarget->SetTime(TimeUnit::FromMicroseconds(mSeekedVideoData->mTime));
     } else if (mIsVideoQueueFinished || VideoQueue().AtEndOfStream()) {
-      mSeekJob.mTarget.SetTime(mDuration);
+      mSeekJob.mTarget->SetTime(mDuration);
     } else {
       MOZ_ASSERT(false, "No data!");
     }
   }
 
   void MaybeFinishSeek()
   {
     if (IsAudioSeekComplete() && IsVideoSeekComplete()) {
       UpdateSeekTargetTime();
 
-      auto time = mSeekJob.mTarget.GetTime().ToMicroseconds();
+      auto time = mSeekJob.mTarget->GetTime().ToMicroseconds();
       DiscardFrames(AudioQueue(), [time] (int64_t aSampleTime) {
         return aSampleTime < time;
       });
 
       OnSeekTaskResolved();
     }
   }
 
@@ -1912,17 +1912,17 @@ StateObject::HandleNotWaited(const WaitF
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 StateObject::HandleSeek(SeekTarget aTarget)
 {
   SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
   SeekJob seekJob;
-  seekJob.mTarget = aTarget;
+  seekJob.mTarget = Some(aTarget);
   return SetSeekingState(Move(seekJob), EventVisibility::Observable);
 }
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::
 StateObject::HandleShutdown()
 {
   return SetState<ShutdownState>();
@@ -1984,35 +1984,35 @@ StateObject::HandleResumeVideoDecoding()
 
   // Start video-only seek to the current time.
   SeekJob seekJob;
 
   const SeekTarget::Type type = mMaster->HasAudio()
                                 ? SeekTarget::Type::Accurate
                                 : SeekTarget::Type::PrevSyncPoint;
 
-  seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
-                               type,
-                               true /* aVideoOnly */);
+  seekJob.mTarget.emplace(mMaster->GetMediaTime(),
+                          type,
+                          true /* aVideoOnly */);
 
   SetSeekingState(Move(seekJob), EventVisibility::Suppressed)->Then(
     AbstractThread::MainThread(), __func__,
     [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
     [](){});
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 StateObject::SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility)
 {
-  if (aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast()) {
+  if (aSeekJob.mTarget->IsAccurate() || aSeekJob.mTarget->IsFast()) {
     return SetState<AccurateSeekingState>(Move(aSeekJob), aVisibility);
   }
 
-  if (aSeekJob.mTarget.IsNextFrame()) {
+  if (aSeekJob.mTarget->IsNextFrame()) {
     return SetState<NextFrameSeekingState>(Move(aSeekJob), aVisibility);
   }
 
   MOZ_ASSERT_UNREACHABLE("Unknown SeekTarget::Type.");
   return nullptr;
 }
 
 void
@@ -2226,17 +2226,17 @@ SeekingState::SeekCompleted()
 
   // Notify FirstFrameLoaded now if we haven't since we've decoded some data
   // for readyState to transition to HAVE_CURRENT_DATA and fire 'loadeddata'.
   if (!mMaster->mSentFirstFrameLoadedEvent) {
     mMaster->FinishDecodeFirstFrame();
   }
 
   // Ensure timestamps are up to date.
-  if (!mSeekJob.mTarget.IsVideoOnly()) {
+  if (!mSeekJob.mTarget->IsVideoOnly()) {
     // Don't update playback position for video-only seek.
     // Otherwise we might have |newCurrentTime > mMediaSink->GetPosition()|
     // and fail the assertion in GetClock() since we didn't stop MediaSink.
     mMaster->UpdatePlaybackPositionInternal(newCurrentTime);
   }
 
   // Try to decode another frame to detect if we're at the end...
   SLOG("Seek completed, mCurrentPosition=%lld", mMaster->mCurrentPosition.Ref());
--- a/dom/media/SeekJob.cpp
+++ b/dom/media/SeekJob.cpp
@@ -3,52 +3,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SeekJob.h"
 
 namespace mozilla {
 
-SeekJob::SeekJob()
-{
-}
-
-SeekJob::SeekJob(SeekJob&& aOther) : mTarget(aOther.mTarget)
-{
-  aOther.mTarget.Reset();
-  mPromise = Move(aOther.mPromise);
-}
-
 SeekJob::~SeekJob()
 {
-  MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
+  MOZ_DIAGNOSTIC_ASSERT(mTarget.isNothing());
   MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
 }
 
-SeekJob& SeekJob::operator=(SeekJob&& aOther)
-{
-  MOZ_DIAGNOSTIC_ASSERT(!Exists());
-  mTarget = aOther.mTarget;
-  aOther.mTarget.Reset();
-  mPromise = Move(aOther.mPromise);
-  return *this;
-}
-
 bool SeekJob::Exists() const
 {
-  MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
-  return mTarget.IsValid();
+  MOZ_ASSERT(mTarget.isSome() == !mPromise.IsEmpty());
+  return mTarget.isSome();
 }
 
 void SeekJob::Resolve(const char* aCallSite)
 {
   mPromise.Resolve(true, aCallSite);
-  mTarget.Reset();
+  mTarget.reset();
 }
 
 void SeekJob::RejectIfExists(const char* aCallSite)
 {
-  mTarget.Reset();
+  mTarget.reset();
   mPromise.RejectIfExists(true, aCallSite);
 }
 
 } // namespace mozilla
--- a/dom/media/SeekJob.h
+++ b/dom/media/SeekJob.h
@@ -10,29 +10,24 @@
 #include "mozilla/MozPromise.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "SeekTarget.h"
 
 namespace mozilla {
 
 struct SeekJob {
-  SeekJob();
-
-  SeekJob(SeekJob&& aOther);
-
-  SeekJob& operator=(SeekJob&& aOther);
+  SeekJob() = default;
+  SeekJob(SeekJob&& aOther) = default;
+  SeekJob& operator=(SeekJob&& aOther) = default;
+  ~SeekJob();
 
   bool Exists() const;
-
   void Resolve(const char* aCallSite);
-
   void RejectIfExists(const char* aCallSite);
 
-  ~SeekJob();
-
-  SeekTarget mTarget;
+  Maybe<SeekTarget> mTarget;
   MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
 };
 
 } // namespace mozilla
 
 #endif /* SEEK_JOB_H */